sml understanding function compositions - sml

I am trying to understand difference between these two;
val my_fun = length o List.filter (fn (item) => item = #"a") o String.explode
This one can be invoked (my_fun "name" will return 1) and works fine. I am trying to understand why following is not working
length o (List.filter (fn (item) => item = #"a" ) (String.explode "name"))
Definition of function composition in sml
f o g = f(g(x))
In the second form what we do is ( I think)
length ([#"a"])

You seem to be confusing function composition with function application.
Composition is a higher-order function which takes two functions, f and g, of compatible types and returns another function -- the function which is computed by first applying the g to a value and then applying f to the result. o is a built-in operator, but if you wanted to define composition yourself it would be something like
fun compose (f,g) x = f(g(x))
this has type fn : ('a -> 'b) * ('c -> 'a) -> 'c -> 'b (which is exactly the type you get when you type (op o); in the REPL). Note that the return value of compose is 'c -> 'b, which is a function type.
length o List.filter (fn (item) => item = #"a") o String.explode
makes perfect sense since the types are compatible and composition is right-associative.
On the other hand, as you already note,
length o (List.filter (fn (item) => item = #"a" ) (String.explode "name"))
would be equivalent to
length o [#"a"]
That really doesn't make sense. What would it even mean to compose a function with a list. A list isn't a function. It does make sense to apply length to that list, which is what you seem to expect.
Application is simply juxtaposition, so all you would need to do is write
length (List.filter (fn (item) => item = #"a" ) (String.explode "name"))
Which reduces to length [#"a"] and from thence to 1.
If you wanted to write your own apply function you would write:
def apply f x = f x
This might strike you as superficially similar to compose but its type is radically different: fn : ('a -> 'b) -> 'a -> 'b. Composition involves application, but it isn't the same thing.

Related

Given these type declarations in OCaml for a stream type, how to I write a flatten function?

exception Empty_list
type 'a stream = Stream of 'a * (unit -> 'a stream) | Nil
let shead s = match s with
| Stream (a, _) -> a
| Nil -> raise Empty_list
let stail s = match s with
| Stream (_, s) -> s ()
| Nil -> raise Empty_list
let stream_to_list s =
let rec stream_to_list s accumulator = match s with
| Nil -> accumulator
| Stream (_, _) -> stream_to_list (stail s) (shead s :: accumulator)
in List.rev (stream_to_list s [])
The flatten function would take an arbitrarily nested set of streams and produce a completely flattened version that's a result of an inorder traversal of the nesting structure and contains only the leaves. This would mean that the result of shead on each member of this new thing would return something that was not a stream.
The idea is that stream_to_list (flatten s) would give back a list in which no element was a stream.
Your desired function is ill-typed and cannot exist in a language with parametric polymorphism like OCaml: 'a stream -> 'b stream where 'b is not a stream is not a valid type.
Parametric polymorphism requires that polymorphic functions does not change their behavior in function of the type of their argument. This useful both in term of semantics, types can be erased once type checking is done, and for type theoretical reason: proofs (aka the program) cannot inspect the theorem (aka the type) that they are trying to prove.
If you want to flatten multiple times a nested streams they are two options:
if the nested level is known statically, you can use flatten multiple times. Note that it is straightforward to flatten a number of time exponential with the size of the code
let flatten16 x =
let flatten2 x = flatten (flatten x) in
let flatten4 x = flatten2 (flatten2 x) in
let flatten8 x = flatten4 (flatten4 x) in
flatten8 (flatten8 x)
This is completely sufficient in practice since types don't grow exponentially in human written code.
If the level of nesting is arbitrary, the level of nesting needs to be visible on the value level (which is already needed to construct the stream). This can be achieved with the following variant type:
type 'a nested_seq = Seq of 'a Seq.t | Nested of 'a Seq.t nested_seq
(Your type is equivalent to Seq.t from the standard library)
Then flattening a 'a nested_seq to a 'a Seq.t is a well-defined notion and that can be done with some polymorphic recursion:
let rec flatten: 'a. 'a nested_seq -> 'a Seq.t = fun x ->
match x with
| Seq x -> x
| Nested s -> Seq.flat_map Fun.id (flatten s)
(* or `Seq.concat (flatten s)` in OCaml 4.13 or with a sequence libray *)
Aside: shead and stail are anti-patterns: in order to safely use those functions, you need to pattern match their future argument, and them discard the matched value in each branches.
In other words,
let head s =
match s with
| Nil -> None
| Stream _ -> Some (shead s)
is both less clear and less safe than
let head s =
match s with
| Nil -> None
| Stream (s, _) -> Some s
(The option functions are useful to compose with generic option functions (from the option monad for instance)).

Subtyping for Yojson element in a yojson list

I meet an error about subtyping.
For this code, List.map (fun ((String goal_feat):> Basic.t) -> goal_feat) (goal_feats_json:> Basic.t list).
I meet the following error in vscode:
This expression cannot be coerced to type
Yojson.Basic.t =
[ Assoc of (string * Yojson.Basic.t) list
| Bool of bool
| Float of float
| Int of int
| List of Yojson.Basic.t list
| Null
| String of string ];
it has type [< String of 'a ] -> 'b but is here used with type
[< Yojson.Basic.t ].
While compiling, I meet the following error.
Error: Syntax error: ')' expected.
If I change the code to List.map (fun ((String goal_feat): Basic.t) -> goal_feat) (goal_feats_json:> Basic.t list), which useq explicit type cast instead of subtyping, then the error disappeared. I can not understand what is the problem with my code when i use subtyping. Much appreciation to anyone who could give me some help.
First of all, most likely the answer that you're looking for is
let to_strings xs =
List.map (function `String x -> x | _ -> assert false) (xs :> t list)
The compiler is telling you that your function is handling only one case and you're passing it a list that may contain many other things, so there is a possibility for runtime error. So it is better to indicate to the compiler that you know that only the variants tagged with String are expected. This is what we did in the example above. Now our function has type [> Yojson.Basic.t].
Now back to your direct question. The syntax for coercion is (expr : typeexpr), however in the fun ((String goal_feat):> Basic.t) -> goal_feat snippet, String goal_feat is a pattern, and you cannot coerce a pattern, so we shall use parenthesized pattern here it to give it the right, more general, type1, e.g.,
let exp xs =
List.map (fun (`String x : t) -> x ) (xs :> t list)
This will tell the compiler that the parameter of your function shall belong to a wider type and immediately turn the error into warning 8,
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a case that is not matched:
(`Bool _|`Null|`Assoc _|`List _|`Float _|`Int _)
which says what I was saying in the first part of the post. It is usually a bad idea to leave warning 8 unattended, so I would suggest you to use the first solution, or, otherwise, find a way to prove to the compiler that your list doesn't have any other variants, e.g., you can use List.filter_map for that:
let collect_strings : t list -> [`String of string] list = fun xs ->
List.filter_map (function
| `String s -> Some (`String s)
| _ -> None) xs
And a more natural solution would be to return untagged strings (unless you really need the to be tagged, e.g., when you need to pass this list to a function that is polymorphic over [> t] (Besides, I am using t for Yojson.Basic.t to make the post shorter, but you should use the right name in your code). So here is the solution that will extract strings and make everyone happy (it will throw away values with other tags),
let collect_strings : t list -> string list = fun xs ->
List.filter_map (function
| `String s -> Some s
| _ -> None) xs
Note, that there is no need for type annotations here, and we can easily remove them to get the most general polymoprhic type:
let collect_strings xs =
List.filter_map (function
| `String s -> Some s
| _ -> None) xs
It will get the type
[> `String a] list -> 'a list
which means, a list of polymorphic variants with any tags, returning a list of objects that were tagged with the String tag.
1)It is not a limitation that coercion doesn't work on patterns, moreover it wouldn't make any sense to coerce a pattern. The coercion takes an expression with an existing type and upcasts (weakens) it to a supertype. A function parameter is not an expression, so there is nothing here to coerce. You can just annotate it with the type, e.g., fun (x : #t) -> x will say that our function expects values of type [< t] which is less general than the unannotated type 'a. To summarize, coercion is needed when you have a function that accepts an value that have a object or polymorphic variant type, and in you would like at some expressions to use it with a weakened (upcasted type) for example
type a = [`A]
type b = [`B]
type t = [a | b]
let f : t -> unit = fun _ -> ()
let example : a -> unit = fun x -> f (x :> t)
Here we have type t with two subtypes a and b. Our function f is accepting the base type t, but example is specific to a. In order to be able to use f on an object of type a we need an explicit type coercion to weaken (we lose the type information here) its type to t. Notice that, we do not change the type of x per se, so the following example still type checks:
let rec example : a -> unit = fun x -> f (x :> t); example x
I.e., we weakened the type of the argument to f but the variable x is still having the stronger type a, so we can still use it as a value of type a.

Monads and SML modules

signature MAPPABLE = sig
type 'a mappable
val fmap : ('a -> 'b) -> 'a mappable -> 'b mappable
end
structure Option : MAPPABLE = struct
type 'a mappable = 'a option
fun fmap f v =
case v of
(SOME x) => SOME (f x)
| NONE => NONE;
end
structure List : MAPPABLE = struct
type 'a mappable = 'a list
fun fmap f v = map f v
end
fun incByFive x = x + 5
Really just to have a function that does stuff with fmap
fun mapToPair f x =
let val b = List.fmap f x
in (b,b)
end
val lst = mapToPair incByFive [1,2,3];
Suppose you want to make a generic implementation, that works for
all instances of MAPPABLE. The following does not work
fun mapToPair f x =
let val b = MAPPABLE.fmap f x
in (b,b)
end
It seems, that SML people point to Functors, if that needs to be done.
I tried implementing one, for a generic implementation of mapToPair
functor FMAPFUNCTOR (structure Q : MAPPABLE)
= struct
fun mapToPair f x =
let val b = Q.fmap f x
in (b,b)
end
end;
However, to use it with what in Haskell I'd call a functor
instance, I need to instantiate the functor (this reminds me of C++
templates for some reason)
structure MAPPABLE_TO_PAIR = FMAPFUNCTOR (structure Q = List);
val lstByPoly = MAPPABLE_TO_PAIR.mapToPair incByFive [1,2,3]
I would have to repeat that instantiation for every MAPPABLE
instance I want to use. I guess Haskell performs something like this,
too. Just implicitly.
Now I wonder if there is any shortcut / sugar for a better "user
experience" in SML that I have missed. Because really, it seems kind
of a lot of boilerplate in the way in order to use this in a code
base.
I guess Haskell performs something like this, too. Just implicitly.
The Haskell standard library defines and imports a ton of type class instances. Given a sufficient set of ML functors, their applications and their implicit compile-time imports, you could achieve something quite convenient.
But Haskell does let you automate type class instance declarations in ways that SML doesn't.
For example, instance Foo t => Bar t where ... is comparable to SML's higher-order functors, but in SML you explicitly have to generate a module that corresponds to Bar t for each concrete Foo t. Haskell also lets you derive instances syntactically.
OCaml had modular implicits from 2014 (example), but they mainly give you syntax sugar to refer to defined functor instances, rather than generate them.
I suspect that the reason ML module systems are still more explicit than Haskell's is because of things like overlapping instances.
Andreas Rossberg contributed 1ML in 2014 in which modules are first-class citizens. That means a function could take a module as an argument, e.g. like this:
;; Higher-kinded polymorphism
type MONAD (m : type => type) =
{
return 'a : a -> m a;
bind 'a 'b : m a -> (a -> m b) -> m b;
};
map 'a 'b (m : type => type) (M : MONAD m) (f : a -> b) mx =
M.bind mx (fun x => M.return (f x));
do map :
'a => 'b => (m : type => type) => (M : MONAD m) => (a -> b) -> m a -> m b;
This is still research-y in the sense that the compiler has "TOY" in its name, but it'd be an example of an ML (although not Standard ML) that does something comparably generic with modules.

Generalized list printing function in SML

I have to write a function to print a list in SML with the following type signature :
val printGenList = fn : ('a -> 'b) -> 'a list -> unit
The "printGenList" will take two arguments function f and list l and applies the function f to each element of list l recursively.
Since I am new to ML, I am not able to implement it but I tried this code which is giving the different type signature
fun printGenList = CONS(fib, fn => printGenList fib fibs);
where, fib is
fun fib a b = CONS(a, fn => fib b (a+b));
and fibs is
val fibs = fib 0 1;
What you're trying to do is impossible, ML's type system doesn't support this type of polymorphism. If you Google, there are some printf libraries for SML that show you how to approach this -- for each type t that you want to print, you'll need to define a separate t -> unit function and compose them together.
EDIT: Oh, I see, you're not looking for a function to print lists of any type, you're looking for a higher order function that applies an 'a->'b function to every element of a list... my mistake. This is what you need:
val rec printGenList =
fn (f : 'a -> 'b) =>
(fn [] => ()
| x::xs => (f(x); printGenList f xs))

ocaml function takes function as parameter and output a function

I need to find a way to combine two functions and output them as one.
I have the following code where take in a list of function ('a->'a) list then output a function ('a->'a) using the List.fold_left.
I figured out the base case, but I tried a lot of ways to combine two functions. The output should have the type ('a -> 'a) list -> ('a -> 'a).
example output:
# pipe [] 3;;
- : int = 3
# pipe [(fun x-> 2*x);(fun x -> x + 3)] 3 ;;
- : int = 9
# pipe [(fun x -> x + 3);(fun x-> 2*x)] 3;;
- : int = 12
function:
let p l =
let f acc x = fun y-> fun x->acc in (* acc & x are functions 'a->'a *)
let base = fun x->x in
List.fold_left f base l
Since you know that you have to use a left fold, you now have to solve a fairly constrained problem: given two functions of type 'a -> 'a, how do you combine them into a single function of the same type?
In practice, there is one general way of combining functions: composition. In math, this is usually written as f ∘ g where f and g are the functions. This operation produces a new function which corresponds to taking an argument, applying g to it and then applying f to the result. So if h = f ∘ g, then we can also write this as h(x) = f(g(x)).
So your function f is actually function composition. (You should really give it a better name than f.) It has to take in two functions of type 'a -> 'a and produce another function of the same type. This means it produces a function of one argument where you produce a function taking two arguments.
So you need to write a function compose (a more readable name than f) of type ('a -> 'a) -> ('a -> 'a) -> ('a -> 'a). It has to take two arguments f and g and produce a function that applies both of them to its argument.
I hope this clarifies what you need to do. Figuring out exactly how to do it in OCaml is a healthy exercise.