Dafny precondition checks in generated code - dafny

I was wondering if there is a way to add precondition checks in the Dafny generated code. For example, let's take the following code snippet:
method MultipleReturns(x: int, y: int) returns (more: int, less: int)
requires 0 < y
ensures less < x < more
{
more := x + y;
less := x - y;
}
I'd like that the resulting code in C++ to have the following check:
assert(0 < y);
If this is not available in Dafny, what options do I have? Thank you!

There is no automatic way to turn the requires clause into a run-time check. The condition of a requires clause is in a ghost context, so it may not be compilable in general. Also, if you verify your program, then you know the condition is always going to evaluate to true, so there's no point in also checking it at run time.
To support situations where the condition to be checked is too difficult to verify statically, and especially to support cross-language situations where the caller is not written in Dafny and therefore not verified, Dafny has expect statements. You can use expect in your example to trade the statically verified precondition for a run-time check:
method MultipleReturns(x: int, y: int) returns (more: int, less: int)
ensures less < x < more
{
RuntimeRequires(0 < y);
more := x + y;
less := x - y;
}
method RuntimeRequires(cond: bool)
ensures cond
{
expect cond, "precondition failure";
}
Note that without the requires clause, you will not get any static verification at call sites.
As one other variation, you could use both a requires clause and the RuntimeRequires method I showed. If you do, I would suggest you change the specification of RuntimeRequires from an ensures to a requires, to make sure you have copied the condition correctly.
method MultipleReturns(x: int, y: int) returns (more: int, less: int)
requires 0 < y
ensures less < x < more
{
RuntimeRequires(0 < y); // try changing this to 10 < y and see what happens
more := x + y;
less := x - y;
}
method RuntimeRequires(cond: bool)
requires cond
{
expect cond, "precondition failure";
}

Related

how to prove that turning a set into a sequence and back is an identity in dafny

Hi Relatively new to Dafny and have defined methods set2Seq and seq2Set for conversion between sets and seqs. But can only find how to write a function fseq2Set from sets to sequences.
I can not find how to define fseq2Set. As Lemmas can not reference methods this makes proving the identity beyond me. Any help much appreciated?
Code:
function method fseq2Set(se: seq<int>) :set<int>
{ set x:int | x in se :: x }
method seq2Set(se: seq<int>) returns (s:set<int>)
{ s := set x:int | x in se :: x; }
method set2Seq(s: set<int>) returns (se:seq<int>)
requires s != {}
ensures s == fseq2Set(se)
decreases |s|
{
var y :| y in s;
var tmp ;
if (s=={y}) {tmp := [];} else {tmp := set2Seq(s-{y});}
se := [y] + tmp;
assert (s-{y}) + {y} == fseq2Set([y] + tmp);
}
/* below fails */
function fset2Seq(s:set<int>):seq<int>
decreases s { var y :| y in s ; [y] + fset2Seq(s-{y}) }
lemma cycle(s:set<int>) ensures forall s:set<int> :: fseq2Set(fset2Seq(s)) == s { }
Ok, there is kind of a lot going on here. First of all, I'm not sure if you intended to do anything with the methods seq2Set and set2Seq, but they don't seem to be relevant to your failure, so I'm just going to ignore them and focus on the functions.
Speaking of functions, Danfy reports an error on your definition of fset2Seq, because s might be empty. In that case, we should return the empty sequence, so I adjusted your definition to:
function fset2Seq(s:set<int>):seq<int>
decreases s
{
if s == {} then []
else
var y := Pick(s);
[y] + fset2Seq(s - {y})
}
function Pick(s: set<int>): int
requires s != {}
{
var x :| x in s; x
}
which fixes that error. Notice that I also wrapped the let-such-that operator :| in a function called Pick. This is essential, but hard to explain. Just trust me for now.
Now on to the lemma. Your lemma is stated a bit weirdly, because it takes a parameter s, but then the ensures clause doesn't mention s. (Instead it mentions a completely different variable, also called s, that is bound by the forall quantifier!) So I adjusted it to get rid of the quantifier, as follows:
lemma cycle(s:set<int>)
ensures fseq2Set(fset2Seq(s)) == s
Next, I follow My Favorite Heuristic™ in program verification, which is that the structure of the proof follows the structure of the program. In this case, the "program" in question is fseq2Set(fset2Seq(s)). Starting from our input s, it first gets processed recursively by fset2Seq and then through the set comprehension in fseq2Set. So, I expect a proof by induction on s that follows the structure of fset2Seq. That structure is to branch on whether s is empty, so let's do that in the lemma too:
lemma cycle(s:set<int>)
ensures fseq2Set(fset2Seq(s)) == s
{
if s == {} {
} else {
}
...
Dafny reports an error on the else branch but not on the if branch. In other words, Dafny has proved the base case, but it needs help with the inductive case. The next thing that fset2Seq(s) does is call Pick(s). Let's do that too.
lemma cycle(s:set<int>)
ensures fseq2Set(fset2Seq(s)) == s
{
if s == {} {
} else {
var y := Pick(s);
...
Now we know from its definition that fset2Seq(s) is going to return [y] + fset2Seq(s - {y}), so we can copy-paste our ensures clause and manually substitute this expression.
lemma cycle(s:set<int>)
ensures fseq2Set(fset2Seq(s)) == s
{
if s == {} {
} else {
var y := Pick(s);
assert fseq2Set([y] + fset2Seq(s - {y})) == s;
...
Dafny reports an error on this assertion, which is not surprising, since it's just a lightly edited version of the ensures clause we're trying to prove. But importantly, Dafny no longer reports an error on the ensures clause itself. In other words, if we can prove this assertion, we are done.
Looking at this assert, we can see that fseq2Set is applied to two lists appended together. And we would expect that to be equivalent to separately converting the two lists to sets, and then taking their union. We could prove a lemma to that effect, or we could just ask Dafny if it already knows this fact, like this:
lemma cycle(s:set<int>)
ensures fseq2Set(fset2Seq(s)) == s
{
if s == {} {
} else {
var y := Pick(s);
assert fseq2Set([y] + fset2Seq(s - {y})) == fseq2Set([y]) + fseq2Set(fset2Seq(s - {y}));
assert fseq2Set([y] + fset2Seq(s - {y})) == s;
...
(Note that the newly added assertion is before the last one.)
Dafny now accepts our lemma. We can clean up a little by deleting the the base case and the final assertion that was just a copy-pasted version of our ensures clause. Here is the polished proof.
lemma cycle(s:set<int>)
ensures fseq2Set(fset2Seq(s)) == s
{
if s != {} {
var y := Pick(s);
assert fseq2Set([y] + fset2Seq(s - {y})) == fseq2Set([y]) + fseq2Set(fset2Seq(s - {y}));
}
}
I hope this explains how to prove the lemma and also gives you a little bit of an idea about how to make progress when you are stuck.
I did not explain Pick. Basically, as a rule of thumb, you should just always wrap :| in a function whenever you use it. To understand why, see the Dafny power user posts on iterating over collecion and functions over set elements. Also, see Rustan's paper Compiling Hilbert's epsilon operator.

For satement in Dafny

I'm confused about Dafny's forall statement if the statement inside the forall is to update a variable such as c.arr[i].d, which is mixed with field and array element access. For instance, the example as follows can't be compiled.
I think, explicitly indicating modifies clause is very tedious, I need indicate: modifies top, modifies top.Cache[i]
datatype CACHE_STATE = I| S| E
datatype MSG_CMD = Empty| ReqS| ReqE| Inv| InvAck| GntS| GntE
type NODE=nat
type DATA=nat
type boolean=bool
class class_0 {
var
Data : DATA,
Cmd : MSG_CMD
}
class class_1 {
var
Data : DATA,
State : CACHE_STATE
}
class TopC{
var
AuxData : DATA,
MemData : DATA,
CurPtr : NODE,
CurCmd : MSG_CMD,
ExGntd : boolean,
ShrSet : array<boolean>,
InvSet : array<boolean>,
Chan3 : array<class_0 > ,
Chan2 : array<class_0 > ,
Chan1 : array<class_0 > ,
Cache : array<class_1 > }
method n_RecvReqS(top:TopC,i:nat, N0:nat )
requires 0<= i<N0
requires top.Chan1.Length ==N0
requires N0>0
requires top.InvSet.Length ==N0
requires N0>0
requires top.ShrSet.Length ==N0
requires N0>0
requires top.Cache.Length ==N0
requires ((top.Chan1[i].Cmd == ReqS) && (top.CurCmd == Empty))
modifies top.Chan1[i]
modifies top.InvSet
modifies top.ShrSet
modifies top
modifies top.Cache
{
top.CurCmd := ReqS;
top.CurPtr := i;
top.Chan1[i].Cmd := Empty;
forall j | 0<= j< N0 {
top.InvSet[j] := top.ShrSet[j];
}
forall j | 0<= j< N0 {
top.Cache[j].State:= I;
}
}
Knowing about a method's effect on the heap is crucial to verification, though writing modifies clauses can be tedious. In your case, you need to account for all of the modifications that method n_RecvReqS performs. As you've noted, this includes every top.Cache[j] objects, for all j's.
One way to include these in your modifies clause is to use a set comprehension:
modifies set j | 0 <= j < top.Cache.Length :: top.Cache[j]
This denotes the set of the objects top.Cache[j], where j ranges among the values satisfying 0 <= j < top.Cache.Length.
Read on for further options.
If you do this often and you consider all of these caches to be part of the representation of a TopC object, then you'll probably want to abstract over the many pieces of your state and put these into a set. If you search the Dafny test suite for declarations like
ghost var Repr: set<object>
you'll find some examples. Then, your entire modifies clause would just be
modifies Repr
If you're not ready to use such a Repr set yet, then there's another simple modifies clause you can write for the cache modifications in your program. If you list a sequence of objects in a modifies clause, Dafny will automatically convert it to the corresponding set of objects. You can convert all of the array elements of top.Cache into a sequence by the expression top.Cache[..]. Thus, to account for the updates in your forall statement, you can write
modifies top.Cache[..]
Unfortunately, Dafny doesn't automatically realize that each top.Cache[j] in the body of your forall statement is indeed an element of the set formed from the sequence top.Cache[..]. It is possible to assist the verifier to help it prove this, but doing so adds some clutter to the forall statement. Here's how I did it:
forall j | 0 <= j < N0 {
(assert top.Cache[j] in top.Cache[..]; top.Cache[j]).State:= I;
}
Rustan

When to use num in Dart

I am new to Dart and I can see Dart has num which is the superclass of int and double (and only those two, since it's a compile time error to subclass num to anything else).
So far I can't see any benefits of using num instead of int or double, are there any cases where num is better or even recommended? Or is it just to avoid thinking about the type of the number so the compiler will decide if the number is an int or a double for us?
One benefit for example
before Dart 2.1 :
suppose you need to define a double var like,
double x ;
if you define your x to be a double, when you assign it to its value, you have to specify it say for example 9.876.
x = 9.876;
so far so good.
Now you need to assign it a value like say 9
you can't code it like this
x = 9; //this will be error before dart 2.1
so you need to code it like
x = 9.0;
but if you define x as num
you can use
x = 9.0;
and
x = 9;
so it is a convenient way to avoid these type mismatch errors between integer and double types in dart.
both will be valid.
this was a situation before Dart 2.1 but still can help explain the concept
check this may be related
Not sure if this is useful to anyone, but I just ran into a case where I needed num in a way.
I defined a utility function like this:
T maximumByOrNull<T, K extends Comparable<K>>(Iterable<T> it, K key(T),) {
return it.isEmpty
? null
: it.reduce((a, b) => key(a).compareTo(key(b)) > 0 ? a : b);
}
And invoking it like this…
eldest = maximumByOrNull(students, (s) => s.age);
… caused trouble when age is an int, because int itself does not implement Comparable<int>.
So Dart cannot infer the type K in the invocation to maximumByOrNull.
However, num does implement Comparable<num>. And if I specified:
eldest = maximumByOrNull(students, (s) => s.age as num); // this
eldest = maximumByOrNull<Student, num>(students, (s) => s.age); // or this
the complaint went away.
Bottom line: it seems num implements Comparable when int and double do not, themselves, and sometimes this causes trouble for generic functions.
A good use case of num are extensions that work with int and double.
As an example I include the extension MinMax on List<num> that provides the getters min and max.
extension MinMax on List<num>{
/// Returns the maximum value or `null` if the list is empty.
num get max {
return (isNotEmpty)
? fold<num>(0, (prev, current) => (prev > current) ? prev : current)
: null;
}
/// Returns the minimum value or `null` if the list is empty.
num get min {
return (isNotEmpty)
? fold<num>(0, (prev, current) => (prev < current) ? prev : current)
: null;
}
}
Using the extension above one can access the min/max values without a need to create specific implementations for the classes int and double.
void main() {
final a = <int>[1,3,5];
final b = <double>[ 0.5, 0.8, -5.0];
print(a.min);
print(b.max);
}
I just ran into a use case when it is useful.
My app stores weight, which was originally defined as a double.
When using with a local database (sqlite) it works fine, since sqlite handles integer and real types.
However, when I converted my app to use Firestore database, I ran into issues with all my double fields. If a decimal value is stored everything works fine. However, when the weight happens to be a whole number, Firestore returns it as an int and suddenly type errors - int is not a subtype of type double - start to appear.
In the above scenario changing the double fields and variables to num was a quite simple solution.

How to optionally pass an optional parameter?

Is there an easier (and cleaner) way to pass an optional parameter from one function to another than doing this:
void optional1({num x, num y}) {
if (?x && ?y) {
optional2(x: x, y: y);
} else if (?x) {
optional2(x: x);
} else if (?y) {
optional2(y: y);
} else {
optional2();
}
}
void optional2({num x : 1, num y : 1}) {
...
}
The one I actually want to call is:
void drawImage(canvas_OR_image_OR_video, num sx_OR_x, num sy_OR_y, [num sw_OR_width, num height_OR_sh, num dx, num dy, num dw, num dh])
At least I don't get a combinatorial explosion for positional optional parameters but I'd still like to have something simpler than lot's of if-else.
I have some code that uses the solution proposed in the first answer (propagating default values of named optional parameters, but I lose the ability to check whether or not the value was provided by the initial caller).
I've been burned by this corner of Dart several times. My current guidelines, which I encourage anyone to adopt are:
Do not use default values. Instead, use the implicit default of null and check for that in the body of the function. Document what value will be used if null is passed.
Do not use the ? argument test operator. Instead, just test for null.
This makes not passing an argument and explicitly passing null exactly equivalent, which means you can always forward by explicitly passing an argument.
So the above would become:
void optional1({num x, num y}) {
optional2(x: x, y: y);
}
/// [x] and [y] default to `1` if not passed.
void optional2({num x, num y}) {
if (x == null) x = 1;
if (y == null) y = 1;
...
}
I think this pattern is cleaner and easier to maintain that using default values and avoids the nasty combinatorial explosion when you need forward. It also avoids duplicating default values when you override a method or implement an interface with optional parameters.
However, there is one corner of the Dart world where this doesn't work: the DOM. The ? operator was designed specifically to address the fact that there are some JavaScript DOM methods where passing null is different from passing undefined (i.e. not passing anything).
If you're trying to forward to a DOM method in Dart that internally uses ? then you will have to deal with the combinatorial explosion. I personally hope we can just fix those APIs.
But if you're just writing your own Dart code, I really encourage you to avoid default values and ? entirely. Your users will thank you for it.
Would positional paremeters be an option four your case?
void optional([num x = 1, num y = 1]) {
}
Since you call optional2 with default parameters anyway, this seems like a good replacement without knowing, what the purpose of the function is

"int -> int -> int" What does this mean in F#?

I wonder what this means in F#.
“a function taking an integer, which returns a function which takes an integer and returns an integer.”
But I don't understand this well.
Can anyone explain this so clear ?
[Update]:
> let f1 x y = x+y ;;
val f1 : int -> int -> int
What this mean ?
F# types
Let's begin from the beginning.
F# uses the colon (:) notation to indicate types of things. Let's say you define a value of type int:
let myNumber = 5
F# Interactive will understand that myNumber is an integer, and will tell you this by:
myNumber : int
which is read as
myNumber is of type int
F# functional types
So far so good. Let's introduce something else, functional types. A functional type is simply the type of a function. F# uses -> to denote a functional type. This arrow symbolizes that what is written on its left-hand side is transformed into what is written into its right-hand side.
Let's consider a simple function, that takes one argument and transforms it into one output. An example of such a function would be:
isEven : int -> bool
This introduces the name of the function (on the left of the :), and its type. This line can be read in English as:
isEven is of type function that transforms an int into a bool.
Note that to correctly interpret what is being said, you should make a short pause just after the part "is of type", and then read the rest of the sentence at once, without pausing.
In F# functions are values
In F#, functions are (almost) no more special than ordinary types. They are things that you can pass around to functions, return from functions, just like bools, ints or strings.
So if you have:
myNumber : int
isEven : int -> bool
You should consider int and int -> bool as two entities of the same kind: types. Here, myNumber is a value of type int, and isEven is a value of type int -> bool (this is what I'm trying to symbolize when I talk about the short pause above).
Function application
Values of types that contain -> happens to be also called functions, and have special powers: you can apply a function to a value. So, for example,
isEven myNumber
means that you are applying the function called isEven to the value myNumber. As you can expect by inspecting the type of isEven, it will return a boolean value. If you have correctly implemented isEven, it would obviously return false.
A function that returns a value of a functional type
Let's define a generic function to determine is an integer is multiple of some other integer. We can imagine that our function's type will be (the parenthesis are here to help you understand, they might or might not be present, they have a special meaning):
isMultipleOf : int -> (int -> bool)
As you can guess, this is read as:
isMultipleOf is of type (PAUSE) function that transforms an int into (PAUSE) function that transforms an int into a bool.
(here the (PAUSE) denote the pauses when reading out loud).
We will define this function later. Before that, let's see how we can use it:
let isEven = isMultipleOf 2
F# interactive would answer:
isEven : int -> bool
which is read as
isEven is of type int -> bool
Here, isEven has type int -> bool, since we have just given the value 2 (int) to isMultipleOf, which, as we have already seen, transforms an int into an int -> bool.
We can view this function isMultipleOf as a sort of function creator.
Definition of isMultipleOf
So now let's define this mystical function-creating function.
let isMultipleOf n x =
(x % n) = 0
Easy, huh?
If you type this into F# Interactive, it will answer:
isMultipleOf : int -> int -> bool
Where are the parenthesis?
Note that there are no parenthesis. This is not particularly important for you now. Just remember that the arrows are right associative. That is, if you have
a -> b -> c
you should interpret it as
a -> (b -> c)
The right in right associative means that you should interpret as if there were parenthesis around the rightmost operator. So:
a -> b -> c -> d
should be interpreted as
a -> (b -> (c -> d))
Usages of isMultipleOf
So, as you have seen, we can use isMultipleOf to create new functions:
let isEven = isMultipleOf 2
let isOdd = not << isEven
let isMultipleOfThree = isMultipleOf 3
let endsWithZero = isMultipleOf 10
F# Interactive would respond:
isEven : int -> bool
isOdd : int -> bool
isMultipleOfThree : int -> bool
endsWithZero : int -> bool
But you can use it differently. If you don't want to (or need to) create a new function, you can use it as follows:
isMultipleOf 10 150
This would return true, as 150 is multiple of 10. This is exactly the same as create the function endsWithZero and then applying it to the value 150.
Actually, function application is left associative, which means that the line above should be interpreted as:
(isMultipleOf 10) 150
That is, you put the parenthesis around the leftmost function application.
Now, if you can understand all this, your example (which is the canonical CreateAdder) should be trivial!
Sometime ago someone asked this question which deals with exactly the same concept, but in Javascript. In my answer I give two canonical examples (CreateAdder, CreateMultiplier) inf Javascript, that are somewhat more explicit about returning functions.
I hope this helps.
The canonical example of this is probably an "adder creator" - a function which, given a number (e.g. 3) returns another function which takes an integer and adds the first number to it.
So, for example, in pseudo-code
x = CreateAdder(3)
x(5) // returns 8
x(10) // returns 13
CreateAdder(20)(30) // returns 50
I'm not quite comfortable enough in F# to try to write it without checking it, but the C# would be something like:
public static Func<int, int> CreateAdder(int amountToAdd)
{
return x => x + amountToAdd;
}
Does that help?
EDIT: As Bruno noted, the example you've given in your question is exactly the example I've given C# code for, so the above pseudocode would become:
let x = f1 3
x 5 // Result: 8
x 10 // Result: 13
f1 20 30 // Result: 50
It's a function that takes an integer and returns a function that takes an integer and returns an integer.
This is functionally equivalent to a function that takes two integers and returns an integer. This way of treating functions that take multiple parameters is common in functional languages and makes it easy to partially apply a function on a value.
For example, assume there's an add function that takes two integers and adds them together:
let add x y = x + y
You have a list and you want to add 10 to each item. You'd partially apply add function to the value 10. It would bind one of the parameters to 10 and leaves the other argument unbound.
let list = [1;2;3;4]
let listPlusTen = List.map (add 10)
This trick makes composing functions very easy and makes them very reusable. As you can see, you don't need to write another function that adds 10 to the list items to pass it to map. You have just reused the add function.
You usually interpret this as a function that takes two integers and returns an integer.
You should read about currying.
a function taking an integer, which returns a function which takes an integer and returns an integer
The last part of that:
a function which takes an integer and returns an integer
should be rather simple, C# example:
public int Test(int takesAnInteger) { return 0; }
So we're left with
a function taking an integer, which returns (a function like the one above)
C# again:
public int Test(int takesAnInteger) { return 0; }
public int Test2(int takesAnInteger) { return 1; }
public Func<int,int> Test(int takesAnInteger) {
if(takesAnInteger == 0) {
return Test;
} else {
return Test2;
}
}
You may want to read
F# function types: fun with tuples and currying
In F# (and many other functional languages), there's a concept called curried functions. This is what you're seeing. Essentially, every function takes one argument and returns one value.
This seems a bit confusing at first, because you can write let add x y = x + y and it appears to add two arguments. But actually, the original add function only takes the argument x. When you apply it, it returns a function that takes one argument (y) and has the x value already filled in. When you then apply that function, it returns the desired integer.
This is shown in the type signature. Think of the arrow in a type signature as meaning "takes the thing on my left side and returns the thing on my right side". In the type int -> int -> int, this means that it takes an argument of type int — an integer — and returns a function of type int -> int — a function that takes an integer and returns an integer. You'll notice that this precisely matches the description of how curried functions work above.
Example:
let f b a = pown a b //f a b = a^b
is a function that takes an int (the exponent) and returns a function that raises its argument to that exponent, like
let sqr = f 2
or
let tothepowerofthree = f 3
so
sqr 5 = 25
tothepowerofthree 3 = 27
The concept is called Higher Order Function and quite common to functional programming.
Functions themselves are just another type of data. Hence you can write functions that return other functions. Of course you can still have a function that takes an int as parameter and returns something else. Combine the two and consider the following example (in python):
def mult_by(a):
def _mult_by(x):
return x*a
return mult_by
mult_by_3 = mult_by(3)
print mylt_by_3(3)
9
(sorry for using python, but i don't know f#)
There are already lots of answers here, but I'd like to offer another take. Sometimes explaining the same thing in lots of different ways helps you to 'grok' it.
I like to think of functions as "you give me something, and I'll give you something else back"
So a Func<int, string> says "you give me an int, and I'll give you a string".
I also find it easier to think in terms of 'later' : "When you give me an int, I'll give you a string". This is especially important when you see things like myfunc = x => y => x + y ("When you give curried an x, you get back something which when you give it a y will return x + y").
(By the way, I'm assuming you're familiar with C# here)
So we could express your int -> int -> int example as Func<int, Func<int, int>>.
Another way that I look at int -> int -> int is that you peel away each element from the left by providing an argument of the appropriate type. And when you have no more ->'s, you're out of 'laters' and you get a value.
(Just for fun), you can transform a function which takes all it's arguments in one go into one which takes them 'progressively' (the official term for applying them progressively is 'partial application'), this is called 'currying':
static void Main()
{
//define a simple add function
Func<int, int, int> add = (a, b) => a + b;
//curry so we can apply one parameter at a time
var curried = Curry(add);
//'build' an incrementer out of our add function
var inc = curried(1); // (var inc = Curry(add)(1) works here too)
Console.WriteLine(inc(5)); // returns 6
Console.ReadKey();
}
static Func<T, Func<T, T>> Curry<T>(Func<T, T, T> f)
{
return a => b => f(a, b);
}
Here is my 2 c. By default F# functions enable partial application or currying. This means when you define this:
let adder a b = a + b;;
You are defining a function that takes and integer and returns a function that takes an integer and returns an integer or int -> int -> int. Currying then allows you partiallly apply a function to create another function:
let twoadder = adder 2;;
//val it: int -> int
The above code predifined a to 2, so that whenever you call twoadder 3 it will simply add two to the argument.
The syntax where the function parameters are separated by space is equivalent to this lambda syntax:
let adder = fun a -> fun b -> a + b;;
Which is a more readable way to figure out that the two functions are actually chained.

Resources