unification with recursive datatypes - recursion

Following the suggestion to use recursive datatypes for a nested structure like a tree I tried to make said recsurive datatyep work in a test program but encountered (yet another, very cryptic for me) error.
My program is this:
datatype 'a tree =
Leaf of { value : 'a }
| Node of { value : 'a, left: 'a tree, right: 'a tree }
fun recursivetreebuilder a n =
if n = 0
then
Leaf a
else
Node (a, recursivetreebuilder(a, n-1), recursivetreebuilder(a, n-1))
So, the function is supposed to build a binary tree of depth n, by recursively calling itself with decremented ns until n is 0.
But I am getting this error:
Can't unify {left: 'a tree, right: 'a tree, value: 'a} with {value: 'b} *
(Int32.int/int -> 'c) * (Int32.int/int -> 'c) (Field 1 missing) Found near if
<( n, 0) then Leaf(a) else Node( a, recursivetreebuilder( ...), ......)
Using recursive datatypes was intended to solve another unification issue when using nested lists. Maybe I should be able to see where the problem is given the explanation to my other question, but I don't yet.
What "Field 1" is the compiler referring to and why can't it unify when recursive datatypes was intended to make it be able to unify different "sub-types" of the same datatype?
Edit
Tried several of the suggested structures, but still getting errors. For example for
datatype 'a tree =
Leaf of 'a
| Node of 'a tree * 'a tree
fun recursivetreebuilder a n =
if n < 0
then
Leaf (a)
else
Node (recursivetreebuilder(a, n-1), recursivetreebuilder(a, n-1))
I get
val printList = fn : Int.int list -> unit
Error- in 'recon_bintree.sml', line 12.
Can't unify 'a with 'a * Int32.int/int (Type variable to be unified occurs in type) Found near if
<( n, 0) then Leaf(a) else
Node( recursivetreebuilder( a, ...), recursivetreebuilder( ...))
Error- in 'recon_bintree.sml', line 12.
Can't unify 'a with 'a * Int32.int/int (Type variable to be unified occurs in type) Found near if
<( n, 0) then Leaf(a) else
Node( recursivetreebuilder( a, ...), recursivetreebuilder( ...))
Error- in 'recon_bintree.sml', line 12.
Can't unify 'a tree with Int32.int/int -> 'b (Incompatible types) Found near if
<( n, 0) then Leaf(a) else
Node( recursivetreebuilder( a, ...), recursivetreebuilder( ...))
Error- in 'recon_bintree.sml', line 12.
Can't unify 'a tree with Int32.int/int -> 'b (Incompatible types) Found near if
<( n, 0) then Leaf(a) else
Node( recursivetreebuilder( a, ...), recursivetreebuilder( ...))
Exception- Fail "Static errors (pass2)" raised

There are two problems here.
The first problem is that — for example — { value : 'a, left: 'a tree, right: 'a tree } is a record type, whereas (a, recursivetreebuilder(a, n-1), recursivetreebuilder(a, n-1)) is a tuple rather than a record. So they don't match; it's just like passing a real to a function expecting an int.
(Pedantic aside: technically tuples actually are records, but very specific ones; (a, b, c) is syntactic sugar for { 1 = a, 2 = b, 3 = c }. For most practical purposes, you can just think of tuples and records as two similar-but-completely-separate ways to combine types. But now you know why the error-message referred to "Field 1".)
The second problem is that you're declaring the function to use currying (fun recursivetreebuilder a n = ...), but then you try to call it with a tuple (recursivetreebuilder(a, n-1)).
One approach is to stick with your datatype definition, and keep the function using currying, and change everything to match those decisions:
datatype 'a tree =
Leaf of { value : 'a }
| Node of { value : 'a, left: 'a tree, right: 'a tree }
fun recursivetreebuilder a n =
if n = 0
then
Leaf { value = a}
else
Node { value = a,
left = recursivetreebuilder a (n-1),
right = recursivetreebuilder a (n-1) }
or change your datatype definition to eliminate the record types, and change the function to eliminate the currying:
datatype 'a tree =
Leaf of 'a
| Node of 'a * 'a tree * 'a tree
fun recursivetreebuilder (a, n) =
if n = 0
then
Leaf a
else
Node (a, recursivetreebuilder(a, n-1), recursivetreebuilder(a, n-1))
or mix-and-match the above. (The fix for the record-vs.-tuple problem is independent of the fix for the currying-vs.-tuple problem.)
Incidentally, I think it's a mistake to include a value both in the Leaf case and in the Node case. With your current definition, it's impossible to have a tree containing exactly 0 or exactly 2 elements.
Instead, I think you should either have empty leaves:
datatype 'a tree =
Leaf
| Node of 'a * 'a tree * 'a tree
or nodes that have child-nodes but no values of their own:
datatype 'a tree =
Leaf of 'a
| Node of 'a tree * 'a tree
or eliminate the distinction between leaves and nodes, and make the children optional:
datatype 'a tree =
Node of 'a * 'a tree option * 'a tree option

Related

Elm - fold on recursive type with list does not compile

I'm following this article on catamorphism and I'm trying to define a fold function for a recursive data type like this
type Node anyType
= Leaf Id (Maybe anyType)
| Tree Id (List (Node anyType))
What i wrote is this:
foldTree fLeaf fTree acc node =
let
recurse =
foldTree fLeaf fTree
in
case node of
Leaf id v ->
let
newAcc = fLeaf acc (id, v)
in
newAcc
Tree id l ->
let
newAcc = fTree acc id
in
l |> List.foldl recurse newAcc
If i don't infer the types for foldTree the function compiles, but it seems to not be usable:
collectIds node =
let
fLeaf acc (id,v) = id :: acc
fTree acc id = id :: acc
in
foldTree fLeaf fTree [] node
throws the following:
TYPE MISMATCH - The 1st argument to `foldTree` is not what I expect:
173| foldTree fLeaf fTree [] node
#^^^^^#
This `fLeaf` value is a:
#List a# -> ( a, b ) -> #List a#
But `foldTree` needs the 1st argument to be:
#Node anyType# -> ( Id, Maybe anyType ) -> #Node anyType#
Auto inferring the types for foldTree makes it not compilable and throws the following:
-- Auto Inferred
foldTree : (c -> (Id, Maybe anyType) -> a) -> (c -> Id -> b) -> c -> Node anyType -> d
TYPE MISMATCH - Something is off with the 1st branch of this `case` expression:
126| newAcc
#^^^^^^#
This `newAcc` value is a:
#a#
But the type annotation on `foldTree` says it should be:
#d#
#Hint#: Your type annotation uses `a` and `d` as separate type variables. Your
code seems to be saying they are the same though. Maybe they should be the same
in your type annotation? Maybe your code uses them in a weird way?
and if I try to follow the hint, still does not compile
foldTree : (c -> (Id, Maybe anyType) -> a) -> (c -> Id -> b) -> c -> Node anyType -> a
TYPE MISMATCH - This function cannot handle the argument sent through the (|>) pipe:
134| l |> List.foldl recurse newAcc
#^^^^^^^^^^^^^^^^^^^^^^^^^#
The argument is:
List #(Node anyType)#
But (|>) is piping it to a function that expects:
List #c#
#Hint#: Your type annotation uses type variable `c` which means ANY type of value
can flow through, but your code is saying it specifically wants a `Node` value.
Maybe change your type annotation to be more specific? Maybe change the code to
be more general?
Read <https://elm-lang.org/0.19.1/type-annotations> for more advice!Elm
TYPE MISMATCH - The 1st argument to `foldl` is not what I expect:
134| l |> List.foldl recurse newAcc
#^^^^^^^#
This `recurse` value is a:
c -> Node anyType -> #a#
But `foldl` needs the 1st argument to be:
c -> Node anyType -> #Node anyType#
#Hint#: Your type annotation uses type variable `a` which means ANY type of value
can flow through, but your code is saying it specifically wants a `Node` value.
Maybe change your type annotation to be more specific? Maybe change the code to
be more general?
I'm stuck. Following the types on the article precisely also does not seem to work. I understand that the code on the article is F# and I'm working on Elm but I thought that in this case it would have been 100% translatable.
Where am I going wrong?
Thanks in advance!
You've flipped the arguments to List.foldl. The fold function takes the value first and the accumulator second, while your recurse function takes the accumulator first and the value second.
The simple fix to this is to eta expand the recurse function and flip the arguments when passing it to foldTree:
recurse v a = foldTree fLeaf fTree a v
Also, interestingly, annotating the type of recurse will make it compile, but obviously produces the wrong result. I didn't pursue this further to understand why, as it was wrong, but the lesson you should take from this is to always annotate your top level functions. This will give you better error messages, but also prevents your code from accidentally compiling but producing the wrong result.

Difference between unit type and 'a in ocaml

What's the difference between unit -> unit and 'a -> 'a in OCaml?
For example:
# let f g a = if (g a > a) then a ;;
val f : (unit -> unit) -> unit -> unit = <fun>
# let f g a = if (g a > a ) then a else a;;
val f : ('a -> 'a) -> 'a -> 'a = <fun>
Why does the first one give unit -> unit and the second 'a -> 'a?
Note that in OCaml, if is an expression: it returns a value.
The key to understand your problem is that
if condition then a
is equivalent to
if condition then a else ()
The typing rules for if are the following:
the condition should have type bool
both branches should have the same type
the whole if expression has the same type as the branches
In other words, in if cond then a, a should have type unit.
unit is the type used when nothing is return by a function (basically, functions who are printing things)
'a is a neutral type. It is a placeholder for any type.
The if expression in the first variant has no else clause, so the true branch of the if expression must have type unit. This means that the type of a must be unit as well, and g must have type unit -> unit.
In the second variant, due to the else clause, the only requirement is that the true and false branches of the if expression have the same type, so the type generalizes, and so does the type of g.

Understanding passing of polymorphic types in Standard ML

I am working on some exercises to help my understanding of SML and find I am having a hard time understanding how generic/polymorphic types are passed into functions.
I am given the following initial information:
datatype 'a tree = Leaf | Node of 'a tree * 'a * 'a tree
val testTree = Node (Node (Node (Leaf, ("a", 107), Leaf), ("c", 417), Node (Leaf, ("e", ~151), Node (Leaf, ("o", ~499), Leaf))), ("s", 35), Node (Leaf, ("u", ~387), Node (Leaf, ("y", 263), Leaf)))
fun nameCompare (n1: name, n2: name) : order = String.compare (n1, n2)
fun treeLookup cmp =
let
fun lkup (x, btree) =
case tree of
Leaf => NONE
| Node (lt, y, rt) =>
(case cmp (x, y) of
LESS => lkup (x, lt)
| EQUAL => SOME y
| GREATER => lkup (x, rt))
in
lkup
end
When I try to call treeLookup I continue to get type matching errors.
For example this is what I may be calling
treeLookup nameCompare ("a", testTree)
and Ill get an error like this
treeLookup nameCompare ("a", testTree);
^^^^^^^^
Type clash: expression of type
(string * int) tree
cannot have type
string tree
What do I need to do in order to satisfy the type of the tree when passing it to treeLookup?
In your tree
a' : ("a", 107)
treeLookup calls the cmp on every element and the one you passed. You passed in nameCompare which takes two strings and a string, and "a" which is a string. That means your tree should only have strings in it.
To solve that you'll probably want to make your tree be a map, effectively comparing only on the first value of the pair:
| Node (lt, (k,v), rt) =>
(case cmp (x, k)
Possibly changing the definition as well:
datatype 'k 'v tree = Leaf | Node of 'k 'v tree * ('k * 'v) * 'k 'v tree
Alternatively, you can change your comparison function to take ('a * 'b), but that means that e.g. you'd need to do treeLookup with an element ("a", 107) which would try to match both fields.
You're comparing a string against an item in the tree, which is a string * int.
You could always change your comparison function; something like
fun nameCompare (n, (k,v)) = String.compare (n1, k)
should do.

Check if a tree is a BST using a provided higher order function in OCAML

So let me start by saying this was part of a past homework I couldn't solve but as I am preparing for a test I would like to know how to do this. I have these implementations of map_tree and fold_tree provided by the instructor:
let rec map_tree (f:'a -> 'b) (t:'a tree) : 'b tree =
match t with
| Leaf x -> Leaf (f x)
| Node (x,lt,rt) -> Node (f x,(map_tree f lt),(map_tree f rt))
let fold_tree (f1:'a->'b) (f2:'a->'b->'b->'b) (t:'a tree) : 'b =
let rec aux t =
match t with
| Leaf x -> f1 x
| Node (x,lt,rt) -> f2 x (aux lt) (aux rt)
in aux t
I need to implement a function that verifies a tree is a BST using the above functions, so far this is what I've accomplished and I'm getting the error:
Error: This expression has type bool but an expression was expected of type
'a tree
This is my code:
let rec smaller_than t n : bool =
begin match t with
| Leaf x -> true
| Node(x,lt,rt) -> (x<n) && (smaller_than lt x) && (smaller_than rt x)
end
let rec greater_equal_than t n : bool =
begin match t with
| Leaf x -> true
| Node(x,lt,rt) -> (x>=n) && (greater_equal_than lt x) && (greater_equal_than rt x)
end
let check_bst t =
fold_tree (fun x -> true) (fun x lt rt -> ((check_bst lt) && (check_bst rt)&&(smaller_than lt x)&&(greater_equal_than rt x))) t;;
Any suggestions? I seem to have trouble understanding exactly how higher order functions work in OCAML
What is the specification of a BST? It's a binary tree where:
all the elements in the left subtree (which is also a BST) are strictly smaller than the value stored at the node
and all the ones in the right subtree (which is also a BST) are bigger or equal than the value stored at the node
A fold is an induction principle: you have to explain how to deal with the base cases (the Leaf case here) and how to combine the results for the subcases in the step cases (the Node case here).
A Leaf is always a BST so the base case is going to be pretty simple. However, in the Node case, we need to make sure that the values are in the right subtrees. To be able to perform this check, we are going to need extra information. The idea is to have a fold computing:
whether the given tree is a BST
and the interval in which all of its values live
Let's introduce type synonyms to structure our thoughts:
type is_bst = bool
type 'a interval = 'a * 'a
As predicted, the base case is easy:
let leaf_bst (a : 'a) : is_bst * 'a interval = (true, (a, a))
In the Node case, we have the value a stored at the node and the results computed recursively for the left (lih as in left induction hypothesis) and right subtrees respectively. The tree thus built is a BST if and only if the two subtrees are (b1 && b2) and their values respect the properties described earlier. The interval in which this new tree's values live is now the larger (lb1, ub2).
let node_bst (a : 'a) (lih : is_bst * 'a interval) (rih : is_bst * 'a interval) =
let (b1, (lb1, ub1)) = lih in
let (b2, (lb2, ub2)) = rih in
(b1 && b2 && ub1 < a && a <= lb2, (lb1, ub2))
Finally, the function checking whether a tree is a BST is defined by projecting out the boolean out of the result of calling fold_tree leaf_bst node_bst on it.
let bst (t : 'a tree) : bool =
fst (fold_tree leaf_bst node_bst t)

Ocaml: weighted graph

I'm working on Ocaml but i'm still a beginner so i have to ask a little help.
Following book instructions, i created a type that represent an oriented graph:
type 'a graph = Gr of ('a * 'a) list;;
let grafo1 = Gr [(1,2);(1,3);(1,4);(2,6);(3,5);(4,6);(6,5);(6,7);(5,4)];;
Then i created a succ function that take a node as input and it give me his successor as output:
let succ (Gr arcs) n=
let rec aux = function
[] -> []
| (x,y):: rest ->
if n = x then y::(aux rest)
else aux rest
in aux arcs;;
Then i used the succ function to make a modified BFS function, this function show me if exists a path between 2 nodes:
let bfs graph p start =
let rec search visited = function
[] -> raise Nodo_not_reachable
|n:: rest ->
if List.mem n visited
then search visited rest
else if p n then n
else search (n::visited) (rest # (succ graph n))
in search [] [start];;
I call the function using this code:
bfs grafo1 (function x -> x=7) 1;;
The function give true as output if exist a path between node 1 and node 7.
Now, i want to do the same things but with a WEIGHTED graph, so i created a new type, a list where each element is composed by 3 numbers instead 2: (node start - wiegh of edge - node arrive):
type 'b graph_w = Grw of ('b * 'b * 'b) list;;
let grafo2 = Grw [(1,3,2);(1,1,5);(2,2,3);(5,5,3);(5,4,6);(3,1,6);(3,7,4);(6,2,7);(4,4,6)];;
So, i modified my previous function to adapt them on this type:
let succ_w (Grw arcs) n=
let rec aux = function
[]-> []
| (x,y,z)::rest ->
if n=x then z::(aux rest)
else aux rest
in aux arcs;;
let bfs_w graph_w p start =
let rec search visited = function
[] -> raise Nodo_non_raggiungibile
|n:: rest ->
if find n visited
then search visited rest
else if p n then n
else search (n::visited) (rest # (succ_w graph_w n))
in search [] [start];;
(Since i can't use List.mem on this new type, i declared a function called find that give me true as output if an element (x,y,z) is contained on a list):
let rec find (x,y,z) = function
[] -> false
| (v,c,p)::rest -> if (x=v) then true else find (x,y,z) rest;;
find (2,3,1) [(2,2,3);(4,5,6);(8,9,0)];;
Now a little problem, someone can tell me how can i call my bfs_w function using the new graph type?
Using
bfs_w grafo2 (function x -> x=7) 1;;
I get the following error:
This expression has type int graph_w but an expression was expected of type ('a * 'b * 'c) graph_w
/--------------------------------/
Okay now the function work correctly thx ^^, but there is another problem: since i want to solve the longhest path problem using bfs (given a start node and a stop node, say true if exists a path between the nodes with a least weight k) i have to implement the (x,y,z) format on my function, so i tried something like this: (is the same function that you suggest but with (x,y,z) instead n:
let bfs_w2 graph_w start stop =
let rec search visited = function
| [] -> raise Node_not_Reachable
| (v,c,p) :: rest ->
if (find (v,c,p) visited) then search visited rest
else if v = stop then true
else search ((v,c,p)::visited) (rest # (succ_w graph_w (v,c,p))) in
search [] [start];;
When i declare the function:
bfs_w2 grafo2 1 4;;
or
bfs_w2 grafo2 (function x -> x=4) 1;;
i met the same error on "grafo2":
This expression has type int graph_w
but an expression was expected of type ('a * 'b * 'c) graph_w
I can't understand where the problem is, the function is almost identical to the one wich you suggest.
ps: i even tried this but i met same result:
let bfs_w2 graph_w p start =
let rec search visited = function
| [] -> raise Nodo_not_reachable
| (x,y,z) :: rest ->
if (List.mem (x,y,z) visited) then search visited rest
else if p (x,y,z) then (x,y,z)
else search ((x,y,z)::visited) (rest # (succ_w graph_w (x,y,z))) in
search [] [start];;
First of all let me define the model of you graph, so that we can speak the same language. You're using a edge list to represent a graph. For a graph of type 'a graph we will say that 'a is node, and 'a * 'a = node * node is an edge. So the graph itself is represented as edge list. To represent a weighted graph you're using edges that are labeled with weights, so the edge has now type node * weight * node, where weight has type int.
Now, let's go to your problem. If you look carefully in bfs implementation you will notice, that visited has type node list (i.e., int list), not edge list. And you're applying your predicate to a value of type node, not to an edge.
The same should be true to your bfs_w function. But here you, for some reason, decided that you're storing edges in a visited list, and used you own find function, instead of List.mem that is fully applicable here.
let bfs_w graph_w p start =
let rec search visited = function
| [] -> raise Not_found
| n :: rest ->
if List.mem n visited
then search visited rest
else if p n then n
else search (n::visited) (rest # (succ_w graph_w n)) in
search [] [start]
This implementation will have a correct type, expecting a user predicate that accepts a value of type node.

Resources