I have a hard time understanding the Type of a certain SML function i need to create.
It is a helper function that should return the longest string from a list of strings.
The type should be: (int * int -> bool) -> string list -> string
How should i read this and how can i create a function that would match?
I started by following simple code:
fun helper(x,y)=x>y
But now i should have this method to return a string list and then a string. But i seem to be missing some points here.
This is the signature of a curried function: (int * int -> bool) -> string list -> string
(int * int -> bool): this is the first argument, and it is a function that receives a tuple of two integers and returns a boolean. Looks like a predicate function.
string list: this is the second argument, a list of strings
string: and this is the resulting type of your function.
For instance, in the course of Programming Languages (which appears to be the source of the question) the function in question should look somewhat like:
fun longest_string_helper f xs = ...
Where
f is a function value of type (int * int -> bool)
xs is a list value of type string list
And of course the function returns a string value.
Notice that the arguments in the function declaration are separated by spaces, and not within a tuple pattern. That evidences that this is a curried function. You can read about this in the course reading notes for section 3, under Another Closure Idiom: Currying and Partial Application (page 11).
Related
This might be a super dumb question, but I don't get it.
What does
(string * string) list -> (string -> string) mean?
Especially the last part (string -> string). How can you achieve that?
I thought it'd be an inner function but it isn't I guess.
string -> string indicates a function which takes a string and returns a string. Similarly, int -> int would indicate a function which takes an int and turns an int.
string * string indicates a tuple of two strings. (string * string) list indicates a list of those tuples.
So:
(string * string) list -> (string -> string)
Indicates a function which takes a list of tuples of two strings, and returns a function which takes a string and returns a string.
It's a good idea to get familiar with this syntax as you'll be seeing a lot of it.
A very simple function that matches this type is shown below. Please note that the pattern-matching contained within is non-exhaustive.
let f : (string * string) list -> (string -> string) =
fun ((a,b)::_) -> fun x -> x ^ a ^ b
When trying this at the top-level, you will see the below type. Due to the way functions work in OCaml, it is equivalent to the type signature you asked about, despite the missing parentheses.
(string * string) list -> string -> string
Some functions in the List module fail when the argument is an empty list. List.rev is an example. The problem is the dreaded Value Restriction.
I met the same problem while trying to define a function that returns a list with all but the last element of a list:
let takeAllButLast (xs: 'a list) =
xs |> List.take (xs.Length - 1)
The function works well with nonempty lists, but a version that would handle empty lists fails:
let takeAllButLast (xs: 'a list) =
if List.isEmpty xs then []
else xs |> List.take (xs.Length - 1)
takeAllButLast []
error FS0030: Value restriction. The value 'it' has been inferred to have generic type
val it : '_a list, etc.
I tried several things: making it an inline function, not specifying a type for the argument, specifying a type for the returned value, making the function depend on a type argument, and using the Option type to obtain an intermediate result later converted to list<'a>. Nothing worked.
For example, this function has the same problem:
let takeAllButLast<'a> (xs: 'a list) =
let empty : 'a list = []
if List.isEmpty xs then empty
else xs |> List.take (xs.Length - 1)
A similar question was asked before in SO: F# value restriction in empty list but the only answer also fails when the argument is an empty list.
Is there a way to write a function that handles both empty and nonempty lists?
Note: The question is not specific to a function that returns all but the last element of a list.
The function itself is completely fine. The function does not "fail".
You do not need to modify the body of the function. It is correct.
The problem is only with the way you're trying to call the function: takeAllButLast []. Here, the compiler doesn't know what type the result should have. Should it be string list? Or should it be int list? Maybe bool list? No way for the compiler to know. So it complains.
In order to compile such call, you need to help the compiler out: just tell it what type you expect to get. This can be done either from context:
// The compiler gleans the result type from the type of receiving variable `l`
let l: int list = takeAllButLast []
// Here, the compiler gleans the type from what function `f` expects:
let f (l: int list) = printfn "The list: %A" l
f (takeAllButLast [])
Or you can declare the type of the call expression directly:
(takeAllButLast [] : int list)
Or you can declare the type of the function, and then call it:
(takeAllButLast : int list -> int list) []
You can also do this in two steps:
let takeAllButLast_Int : int list -> int list = takeAllButLast
takeAllButLast_Int []
In every case the principle is the same: the compiler needs to know from somewhere what type you expect here.
Alternatively, you can give it a name and make that name generic:
let x<'a> = takeAllButLast [] : 'a list
Such value can be accessed as if it was a regular value, but behind the scenes it is compiled as a parameterless generic function, which means that every access to it will result in execution of its body. This is how List.empty and similar "generic values" are implemented in the standard library.
But of course, if you try to evaluate such value in F# interactive, you'll face the very same gotcha again - the type must be known - and you'll have to work around it anyway:
> x // value restriction
> (x : int list) // works
I have a pretty simple code that's supposed to transform a list of tuples (int * string), into two lists, one list of ints and one list of strings - basically a list of tuples into a tuple of lists.
fun unzip_single_int[] : int list = []
| unzip_single_int(x::xs) : int list =
x :: unzip_single_int(xs)
fun unzip_single_string[] : string list = []
| unzip_single_string(x::xs) : string list =
x :: unzip_single_string(xs)
fun unzip[] : (int list * string list) = ([], [])
| unzip([twopls]) : (int list * string list) =
let
val x : int list = unzip_single_int(twopls);
val y : string list = unzip_single_string(twopls); (* this is line 28 btw *)
in
(x, y)
end
And the error:
zip.sml:28.7-28.52 Error: operator and operand don't agree [tycon mismatch]
operator domain: string list
operand: int list
in expression:
unzip_single_int twopls
For some reason the compiler believes val y : string list = unzip_single_string(twopls) is referring to an int list.
Interestingly enough, when I switch the two around, when I change:
val x : int list = unzip_single_int(twopls);
val y : string list = unzip_single_string(twopls);
to
val y : string list = unzip_single_string(twopls);
val x : int list = unzip_single_int(twopls);
The error switches too:
zip.sml:28.7-28.47 Error: operator and operand don't agree [tycon mismatch]
operator domain: int list
operand: string list
in expression:
unzip_single_int twopls
For some reason, whatever the second call is, it's going to assume that its whatever the last call's type was. Why is it doing this? How do I overcome this? I made it very clear in the two other functions definitions that they are int lists and string lists respectively, so why does SML think that I'm sending it a string list when I clearly defined it as an int list ?
Thanks!
The answer to your question is type inference. You haven't given any type for twopls or an argument type for unzip, and you are calling unzip_single_int(twopls) when you make a value declaration for x. Therefore, SML infers that twopls is an int list, because the type of your function unzip_single_int is int list -> int list, which means it takes an int list as input and returns an int list.
After SML infers that twopls is an int list, you are trying to call unzip_single_string(twopls). However, the type of the function unzip_single_string is string list -> string list, therefore it expects an expression of string list as an input. But now twopls is an int list, so you get a type error.
However, your function definitions don't change anything about the list, they return the exact same list, I'm guessing you want them to return either the first or the second element in the tuple, so you should add that. Your unzip function is supposed to have the type (int * string) list -> int list * string list. Therefore, try to pattern match on the int * string elements you have in your list. Normally you would pattern match a list like x :: xs, but if you know the elements are tuples and if you want to access them, you can pattern match them like (num, str) :: xs, where num is an int variable and str is a string variable. You should be able to figure out the rest. Good luck!
First of all I usually programming in imperative languaes, that makes me hard to explain certain things. First of all is functions without args, and return types. Example is function that flattens a list:
# let rec flat = function
[] -> []
| h :: t -> h # flat t;;
val flat : 'a list list -> 'a list = <fun>
How OCaml interpreter know that:
My function flat need exactly one argument which is "list of lists".
Flat returns type is a list. Do the interpreter checks it with [] -> [] line?
let rec flat = function
[] -> []
| h :: t -> h # flat t;;
You used function keyword. function is a shortcut for match ... with. So the function you wrote is exactly like
let rec flat l =
match l with
[] -> []
| h :: t -> h # flat t
That's why ocaml knows your function has one parameter
Your function is recursive. [] -> [] is the basic case and it is also where the function will be stopped. And yes, interpreter checks it with [] -> [].
Furthermore, a function must have at least a unit parameter which is () or a normal parameter. If a function does not have anything, it is not a function, instead, it is a variable with a fixed value.
Let's have an example:
let f1 = Random.int 10;
f1 does not have any parameter, even without a () (here () is just like a method in Java without any parameter). Then f1 is a constant value which was generated by the Random. No matter when you call it, f1 will always be fixed.
let f2 () = Random.int 10;
f2 is a function. And each time you call f2(), the Random inside will generate a random in and returns it.
let rec flat = function
[] -> []
| h :: t -> h # flat t;;
Let's go through this a step at a time. The function keyword, as you might expect, gives a function. The basic syntax is function | pat1 -> branch1 | pat2 -> branch2, and what you get is a function of one argument that tries to match that argument against each pattern in turn, and for the first pattern that matches the result is the corresponding branch.
So that's how we know flat is a function. Moreover, we can see that its one argument is matched against [], which is a list. So flat must be a function that takes a list. We see that if the input is [] then the output is [], so it's a function that takes a list and returns a list.
Now let's look at that second pattern. h :: t is a pattern that matches a list and creates two new variable bindings: h is the first element of the list and t is all the rest of the elements. In particular, h has whatever type the elements of the input list have.
If you look at what happens if this pattern match succeeds, h # flat t, we see the list concatenation operator # applied to h and flat t. This means that h must be a list, and must be the same kind of list as flat t. So the elements of the input list are lists, and so is the output of the function.
This gives you flat : 'a list list -> 'a list.
To answer your questions directly, flat needs exactly one argument because it is defined with the function keyword and the return values of the branches are values and not functions (if the function branches were also functions, that would meant flat could have two or more arguments). It is a list because the pattern match is against list constructors, and it is a list of lists because h is an element of the list and is used with the # operator, which requires its arguments to be lists, so the elements of the list are lists.
There are actually three reasons why the return type must be a list:
In the first branch, a list [] is returned.
In the second branch, the result of # is returned, and # returns lists
Also in the second branch, flat t is called recursively, and then given as an argument to #. Since it is an argument to #, it must be a list, and so flat must return a list.
The third bullet point is especially interesting, because it shows you that it's not just how you create values that determines their type, but how you use them as well.
How to declare a function suffixsen : string list -> string list ?
After declaring types inside the parens, declare the function's return type on the outside with :return-type. At least in SMLnj. I found this through trial and error, can't find documentation for it.
fun suffixson (xs: string list ): string list =
map (fn x => x ^ "son") xs
The syntax to define a function with one argument in sml is:
fun functionName argumentName = functionBody
or
fun functionName (argumentName : argumentType) = functionBody
if you want to specify the type explicitly. So to define a function named suffixsen of type string list -> string list, you can do:
fun suffixsen (strings : string list) = someExpressionThatReturnsAStringList
Edit in response to you comment:
In order to append "son" to each string in the list, you should look at the ^ operator[1], which concatenates string, and the map function which performs an operation for each element in a list.
[1] http://www.standardml.org/Basis/string.html#SIG:STRING.^:VAL (copy and paste this link in your browser - for some reason I can't get this to be clickable)