In the book "Introduction to functional programming" (Bird/Wadlers), I came across the following exercise:
Deduce the type of fix f x = f (fix f) x
Following the book's instructions to deduce the type, I made the following deductions:
Starting on the rhs:
f :: t1
x :: t2
f (fix f) x :: t3
Therefore, fix :: t1 -> t2 -> t3
Keep on deducing types on the lfs:
f :: t4 -> t2 -> t3
fix f :: t4
f :: t6
fix :: t6 -> t4
(1) t6 = t2 and t4 = t3
t1 = (t2 -> t3) -> t2 -> t3
Therefore,
fix :: ((t2 -> t3) -> t2 -> t3) -> t2 -> t3
Substituting by letters,
fix :: ((a -> b) -> a -> b) -> a -> b
Which is exactly what I get from the interpreter when I ask for the type of fix.
However, I would like to know if my reasoning is correct and if my assumptions in (1) are correct and would be what the interpreter would infer.
Related
I am very new to Haskell and I wrote a Data Type in Haskell
for representing an interval map.
What does that mean? Briefly: A map data type that gives you a value back
for every possible key (put simply in my case [0..]).
Then you insert "sequences" like I want my map to hold from 7 to 23 'b'
so keys 0 to 6 will be init value e.g. 'a' and 7 to 23 will be 'b' and 24 and ongoing will be 'a' again etc.
I managed to wrote the Data Type, a get and insert function as well as a
functor version.
But I can't managed to get a applicative functor version to work.
The idea is to set the keys value to [0..] and just work on the values.
Here is my code and thanks for any provided help!
-- Building an interval map data structure in haskell
data IntervalMap k v = IntervalMap {keys :: [k] , values :: [v]} | Empty deriving Show
-- k = key, Typ variable
-- v = value, Typ variable
singleton :: (Enum k, Num k) => v -> IntervalMap k v
singleton v = IntervalMap{keys=[0..], values= repeat v}
-- get operator => a ! 5 = value at position 5
(!) :: Ord k => IntervalMap k v -> k -> v
(!) iMap k = snd (head (filter (\(x, y) -> x == k) (zip (keys iMap) (values iMap)) ))
-- insert a sequence into intervalMap
insert :: (Ord k, Num k, Enum k) => k -> k -> v -> IntervalMap k v -> IntervalMap k v
insert start end value iMap = IntervalMap {keys=keys iMap, values = rangeChanger (values iMap) start end value}
-- helper function to change a range of values in an intervalMap
rangeChanger :: (Num a1, Enum a1, Ord a1) => [a2] -> a1 -> a1 -> a2 -> [a2]
rangeChanger iMapValues start end value = [if (i >= start) && (i <= end) then newValue else iMapValue | (iMapValue, newValue, i) <- zip3 iMapValues (repeat value) [0..]]
-- functor instance for intervalMap
instance Functor (IntervalMap k) where
-- fmap :: (a -> b) -> f a -> f b
fmap f iMap = IntervalMap {keys=keys iMap, values= map f (values iMap) }
-- applicative functor for intervalMap
instance (Ord k, Num k, Enum k) => Applicative (IntervalMap k) where
pure k = IntervalMap{keys=[0..], values=repeat k}
_ <*> Nothing = Nothing
-- HOW TO DO?
-- class Functor functor => Applicative functor where
-- pure :: a -> functor a
-- (<*>) :: functor (a -> b) -> functor a -> functor b
-- (*>) :: functor a -> functor b -> functor b
-- (<*) :: functor a -> functor b -> functor a
It seems like you always expect the keys to be [0..], e.g. it is hard-coded in your rangeChanger function. If that is the case then it is redundant and honestly I would leave it out. You can easily reconstruct it by doing something like zip [0..] (values iMap) as you do in the rangeChanger function.
If you make that change, then your IntervalMap data structure is basically the same as ZipList which has an applicative instance here:
instance Applicative ZipList where
pure x = ZipList (repeat x)
liftA2 f (ZipList xs) (ZipList ys) = ZipList (zipWith f xs ys)
You see that this doesn't define a <*> but that can be defined in terms of liftA2: p <*> q = liftA2 (\f x -> f x) p q, so you could also write that explicitly for ZipList:
ZipList fs <*> ZipList xs = ZipList (zipWith (\f x -> f x) fs xs)
Edit: I should also mention that one difference with ZipList is that you have an Empty constructor for your IntervalMap type. That makes things harder, you would need to know that your values have some sort of default value, but that is not possible in general (not every type has a default value), so your type cannot be an Applicative. Do you really need that Empty case?
There seems to be a difference in the evaluation order of applicative do notation / ado vs. applicative lifting via <$>/map on the first argument, and <*>/apply for remaining arguments.
At least, this is what I have read so far and what is reflected in the course of the exercise shown below. Questions:
Why is the evaluation order of solution 1 and 2 different (general concepts)?
How can I rewrite solution 2 (without ado), respecting the preorder assertion from the test?
Given
Exercise from the PureScript by Example book (Chapter 7) can be found here:
3.(Medium) Write a function traversePreOrder :: forall a m b. Applicative m => (a -> m b) -> Tree a -> m (Tree b) that performs a pre-order traversal of the tree. [...] Applicative do notation (ado) is the easiest way to write this function.
Algebraic data type Tree:
data Tree a
= Leaf
| Branch (Tree a) a (Tree a)
Test expecting the traverse order [1,2,3,4,5,6,7]:
Assert.equal (1 .. 7)
$ snd
$ runWriter
$ traversePreOrder (\x -> tell [ x ])
$ Branch (Branch (leaf 3) 2 (leaf 4)) 1 (Branch (leaf 6) 5 (leaf 7))
Note: I am not sure, what tell and runWriter exactly do - this is a copied code block from the exercise.
For illustration - the example tree looks like this:
What I tried
Solution 1: ado (works)
traversePreOrder :: forall a m b. Applicative m => (a -> m b) -> Tree a -> m (Tree b)
traversePreOrder f Leaf = pure Leaf
traversePreOrder f (Branch tl v tr) = ado
ev <- f v
etl <- traversePreOrder f tl
etr <- traversePreOrder f tr
in Branch etl ev etr
Solution 2: conventional lifting (does not work)
traversePreOrder :: forall a m b. Applicative m => (a -> m b) -> Tree a -> m (Tree b)
traversePreOrder f Leaf = pure Leaf
traversePreOrder f (Branch tl v tr) =
let
ev = f v -- I consciously tried to place this evaluation first, does not work
etl = traversePreOrder f tl
etr = traversePreOrder f tr
in
Branch <$> etl <*> ev <*> etr
This triggers the error:
expected [1,2,3,4,5,6,7], got [3,2,4,1,6,5,7]
let
ev = f v -- I consciously tried to place this evaluation first, does not work
etl = traversePreOrder f tl
etr = traversePreOrder f tr
in
Branch <$> etl <*> ev <*> etr
Source order does not matter in functional programming. You could place these let declarations in any order, it would work the same - they would create the same values, and these values would describe the same computation, and will form the same programs when used in the same expressions.
The "evaluation order" that actually matters here is a property of the applicative functor you're using - the order in which applicative effects are applied. The order is governed by the operators from the Applicative typeclass which you are using here, namely <*>: it is documented to first apply the effects from the left hand side, then the effects from the right hand side. To implement pre-order traversal, you will therefore have to write
traversePreOrder :: forall a m b. Applicative m => (a -> m b) -> Tree a -> m (Tree b)
traversePreOrder f Leaf = pure Leaf
traversePreOrder f (Branch tl v tr) =
(\ev etl etr -> Branch etl ev etr) <$> f v <*> traversePreOrder f tl <*> traversePreOrder f tr
(Disclaimer: I don't know PureScript very well, but it looks very much like Haskell and seems to work the same here.)
Usually test which contain question about SML have questions that ask you to find the signature/type of a function.
For example - What is the type of the following function:
fun foo f g x y = f (f x (g x) y) y;
Solution:
val foo = fn : ('a -> 'b -> 'b -> 'a) -> ('a -> 'b) -> 'a -> 'b -> 'b -> 'a
I was wondering if there is a good algorithm I could follow in order to solve those kind of questions. Every time I try to solve one of those, I get confused and fail.
Start with what you know, then figure out a bit here and a bit there until there are no unknowns.
Here is one possibility:
Call the unknown types FOO, F, G, X, and Y, respectively.
Then look for something small and easy and start assigning types.
(g x)
is clearly an application of a function to one argument.
Set X = a and G = a -> b.
Then look at the enclosing expression:
(f x (g x) y)
| |
v v
a b
So far, we know that F = a -> b -> Y -> C, for some C.
Go outwards again:
f (f x (g x) y) y
Since both x and (f x (g x) y) are first arguments to f, they must be the same type a, and the same idea applies to y and (g x), giving them the type b.
So, F = a -> b -> b -> a and, since the outer f is only given two arguments, the type of the right-hand side must be b -> a.
Thus
X = a
Y = b
G = a -> b
F = a -> b -> b -> a
FOO = (a -> b -> b -> a) -> (a -> b) -> a -> b -> (b -> a)
And, since arrows associate to the right, FOO is equivalent to
(a -> b -> b -> a) -> (a -> b) -> a -> b -> b -> a
There are several ways to derive the type of a function depending on how close to the compiler's algorithm you want to go and how much you want to cut corners with intuition, which can come handy in practice and perhaps in exams, depending on the focus of the exam.
An example by IonuČ› G. Stan cuts very few corners, and has a quite verbose notation. This mechanical approach is very safe, spells out everything and takes some time.
This current example by molbdnilo takes a middle ground and does some equational reasoning, but also relies on some level of intuition. I think this is generally the way you want to be able to do it, since it takes less time and space by hand.
An example by me links to various other examples for a diversity in practical approaches.
I am trying to use List.map in recursive definition, mapping over a list using currently defined recursive function as an argument. Is it possible at all? I can define my own recursive fixpoint definition instead of using map but I am interested in using map here.
Require Import Coq.Lists.List.
Import ListNotations.
Inductive D: nat -> Type := | D0 (x:nat): D x.
Inductive T: nat -> nat -> Type :=
| T0 {i o} (foo:nat): T i o
| T1 {i o} (foo bar:nat) : T i o -> T i o.
Fixpoint E {i o: nat} (t:T i o) (x:nat) (d:D i): option (D o)
:=
(match t in #T i o
return D i -> option (D o)
with
| T0 _ _ foo => fun d0 => None
| T1 _ _ foo bar t' =>
fun d0 =>
let l := List.map (fun n => E t' x d0) [ 1 ; 2 ; 3 ] in
let default := Some (D0 o) in
List.hd default l
end) d.
The example above is artificial, but demonstrates the problem. The error message:
The term "l" has type "list (option (D n0))"
while it is expected to have type "list (option (D o))".
You just need to bind the names on the T1 pattern:
Require Import Coq.Lists.List.
Import ListNotations.
Inductive D: nat -> Type := | D0 (x:nat): D x.
Inductive T: nat -> nat -> Type :=
| T0 {i o} (foo:nat): T i o
| T1 {i o} (foo bar:nat) : T i o -> T i o.
Fixpoint E {i o: nat} (t:T i o) (x:nat) (d:D i): option (D o)
:=
(match t in #T i o
return D i -> option (D o)
with
| T0 _ _ foo => fun d0 => None
(* \/ change here *)
| T1 i o foo bar t' =>
fun d0 =>
let l := List.map (fun n => E t' x d0) [ 1 ; 2 ; 3 ] in
let default := Some (D0 o) in
List.hd default l
end) d.
The problem is that omitting the binders means that the o used on the T1 branch refers to the "outer" variable of the same name, whereas you want it to refer to the one given by T1.
I have a data structure,
datatype 'a tree = Leaf | Branch of 'a tree * 'a * 'a tree
and I want to write a function that traverses this tree in some order. It doesn't matter what it does, so it could be a treefold : ('a * 'b -> 'b) -> 'b -> 'a tree -> 'b. I can write this function like this:
fun treefold f acc1 Leaf = acc1
| treefold f acc1 (Branch (left, a, right)) =
let val acc2 = treefold f acc1 left
val acc3 = f (a, acc2)
val acc4 = treefold f acc3 right
in acc4 end
But because I inevitably have two branches in the last case, this is not a tail-recursive function.
Is it possible to create one that is, given the type signature is allowed to be expanded, and at what cost? I also wonder if it's even worth trying; that is, does it give any speed benefits in practice?
You can achieve a tail-recursive treefold using continuation-passing style:
fun treefold1 f Leaf acc k = k acc
| treefold1 f (Branch (left, a, right)) acc k =
treefold1 f left acc (fn x => treefold1 f right (f(a, x)) k)
fun treefold f t b = treefold1 f t b (fn x => x)
For example:
fun sumtree t = treefold op+ t 0
val t1 = Branch (Branch(Leaf, 1, Leaf), 2, Branch (Leaf, 3, Leaf))
val n = sumtree t1
results in n = 6 as expected.
Like #seanmcl writes, the systematic way to convert a function to be tail-recursive is to use continuation-passing style.
After that you probably want to reify your continuations and use a more concrete data type, like a list for instance:
fun treefoldL f init tree =
let fun loop Leaf acc [] = acc
| loop Leaf acc ((x, right) :: stack) =
loop right (f(x,acc)) stack
| loop (Branch (left, x, right)) acc stack =
loop left acc ((x, right) :: stack)
in loop tree init [] end