declare function type in function definition in haskell - haskell

I just began to learn haskell recently. I have some confusion of declare function type in the function definition. For example if we want to define a foldr function:
foldr :: (a->b->b)->b->[a]->b
foldr f v [] = v
foldr f v (x:xs) = f x (foldr f v xs)
Thus, foldr (+) 0 [1,2,3,4] = sum [1,2,3,4] = 10.
My question is: can I just change it to something like :
foldr :: (a->a->a)->a->[a]->a
OR:
foldr :: (a->b->c)->x->[y]->z
I didn't find a place to explain the choice/requirements of letters in the definition. Thanks!
Update:
I tested it with:
foldr :: (a->b->c)->x->[y]->z
Got the following err:
Couldn't match type `y' with `a'
`y' is a rigid type variable bound by
the type signature for folderr :: (a -> b -> c) -> x -> [y] -> z
at test.hs:3:1
`a' is a rigid type variable bound by
the type signature for folderr :: (a -> b -> c) -> x -> [y] -> z
at test.hs:3:1
If changed everything to a, it works. I must have missing some points here.
As explained in #sepp2k's comments. I tested using the following simple example:
folderr :: (a->b->b)->b->[a]->b
folderr f v [] = v
folderr f v (x:xs) = f x (folderr f v xs)
myop :: Int -> Bool -> Bool
myop x y | x > 0 = True && y
| otherwise = False && y
folderr (myop) True [3,3,222] -> True
folderr (myop) True [3,2,-1] -> False
So a,b just means the types are different or not.

The specific names of your type variables do not matter, that is the types a -> b -> c, x -> y -> z and foo -> bar -> baz are all completely equivalent.
What does matter is whether you use the same type variable multiple times or you use different ones. That is a -> b is a different type than a -> a. Specifically a -> b is more general because out of the concrete types Int -> Int, String -> String, Int -> String and String -> Int, a -> a would only match the first two (with a = Int and a = String respectively) whereas a -> b would match all four.

Related

Haskell: for every even appearance in an array, concatenate an int to the final list

I'm currently trying to write a function that takes as arguments an Int and an array of Ints and for every even value in the array, it concatenates the Int to the final array.
So, something like this:
f 3 [1,2,3,4,5,6] = [1,2,3,3,4,3,5,6,3]
This is the code I imagined would work (I'm just beginning so sorry if it's bad):
f :: Int -> [Int] -> [Int]
f(x,[]) = []
f(x,y)
|even head(y) = (head(y) ++ [x] ++ f(x,drop 1 y)
|otherwise = head(y) ++ f(x,(drop 1 y))
The error I'm getting is "Couldn't match expected type of 'Int' with actual type (a3, [[a3]])'. I understand the parameters types are mismatched, but I'm not sure how a proper syntax would look like here
You use (x, []), so that means the input type would be a tuple, so f :: (Int, [Int]) -> [Int].
I would also use pattern matching instead of head and tail, so:
f :: Int -> [Int] -> [Int]
f _ [] = []
f x (y:ys)
| even y = y : x : f x ys
| otherwise = y : f x ys
You can also generalize the type signature, and work with an inner function to avoid passing the x each time:
f :: Integral a => a -> [a] -> [a]
f x = go
where go [] = []
go (y:ys)
| even y = y : x : go ys
| otherwise = y : go ys
Another way of looking at this would be using a right fold to insert the desired element after even numbers.
f :: Int -> [Int] -> [Int]
f x lst = foldr (\y i -> if even y then y:x:i else y:i) [] lst
Which we can simplify to:
f :: Int -> [Int] -> [Int]
f x = foldr (\y i -> if even y then y:x:i else y:i) []
Note that without specifying the type, the more general inferred type of f would be:
f :: (Foldable t, Integral a) => a -> t a -> [a]

Understanding types in Haskell (lambda epxressions and higher order functions)

I'm currently doing a course in Haskell, and I have a lot of difficulty understanding the types of functions, particularly when there's function application or lambda expressions. Say for instance the following:
f = (\x -> \y -> \z -> [x (y z), y z])
or
g = \x -> \y -> \z -> x.y.z
I can sort of make some assumptions about the fact that x and y are functions, but I don't have a concrete method for figuring out the types of these functions.
Similarly for the following:
h = foldr (&&)
I try to guess and then check via :t in the interpreter, but I'm usually off by quite a bit.
Is there any particular method I can use to find the types of such functions?
You start by assigning type variables to the inputs and the result
f = (\x -> \y -> \z -> [x (y z), y z])
and conclude
f :: a -> b -> c -> d -- (A0)
-- or even (f is not needed)
\x -> \y -> \z -> [x (y z), y z] :: a -> b -> c -> d
that is
x :: a -- (1)
y :: b -- (2)
z :: c -- (3)
[x (y z), y z] :: d -- (4)
You can continue with (4) and conclude
that the type d is a list of d1s, i.e. d ~ [d1] (5)
f :: a -> b -> c -> [d1] -- (A1)
and that the values of the list are of type d1, i.e.
x (y z) :: d1 -- (6)
y z :: d1 -- (7)
From (6) you learn that
x :: e -> d1 -- (8)
y z :: e -- (9)
(1) and (8) unify, i.e. a ~ (e -> d1) and
f :: (e -> d1) -> b -> c -> [d1] -- (A2)
You play this game until you get bored and use GHCi to arrive at
f :: (d1 -> d1) -> (f -> d1) -> f -> [d1] -- (A3)
-- and renaming
f :: (a -> a) -> (b -> a) -> b -> [a] -- (A4)
If you want to learn more and read a paper you can start with Principal type-schemes for functional programs.
Prelude> :t h
h :: Foldable t => Bool -> t Bool -> Bool
Prelude> :t foldr
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Prelude> :t (&&)
(&&) :: Bool -> Bool -> Bool
Prelude>
By "plugging in" (&&) you have removed (a -> b -> b)
so you need to provide the rest to the function
b -> t a -> b
That is restricted by (&&) to be a bool as second param to it, and the second parameter is the t a which is also restricted to being a bool. since a and b needs to be the same type as in the (a->b->b) function.

How would you declare the types of these functions in Haskell?

So I'm learning about Haskell at the moment, and I came across this question:
The type of a function f is supposed to be a->[a]->a. The
following definitions of f are incorrect because their types are all
different from a->[a]->a:
i. f x xs = xs
ii. f x xs = x+1
iii. f x xs = x ++ xs
iv. f x (y:ys) = y
My answers as I see it are:
i) f :: a -> a -> a
This is because x or xs can be of any value and is not a list as it does not contain the ':' operator.
ii) f :: Int -> a -> Int
This is because the + operator is used on x, meaning x is of type Int.
iii) f :: Eq a => a -> a -> a
The ++ operators are used, therefore in order to concatenate they must be of the same type..?
iv) f :: a -> [a] -> a
f returns an element from the list.
The last one is definitely wrong, because it can't be of type a -> [a] -> a. Are there any others I did wrong, and why? I'm hoping I can fully understand types and how to find out the types of functions.
i) f :: a -> a -> a
f x xs = xs
This is because x or xs can be of any value and is not a list as it does not contain the ':' operator.
True, but it also does not have to be of the same type!
So, it's actually f :: a -> b -> b.
ii) f :: Int -> a -> Int
f x xs = x+1
This is because the + operator is used on x, meaning x is of type Int.
Correct. (Actually, in Haskell we get Num b => b -> a -> b which generalized the Int to any numeric type, but it's not that important.)
iii) f :: Eq a => a -> a -> a
f x xs = x ++ xs
The ++ operators are used, therefore in order to concatenate they must be of the same type..?
True, but they must be lists. Also, Eq is only needed if you use == or something which compares values.
Here, f :: [a] -> [a] -> [a].
iv) f :: a -> [a] -> a
f x (y:ys) = y
f returns an element from the list.
The type of x does not have to be the same. We get f :: b -> [a] -> a.
i. f x xs = xs
(...)
i) f :: a -> a -> a
Although this can be a type signature, you make it too restrictive. The function takes two parameters x and xs. Initially we can reason that x and xs can have different types, so we say that x :: a, and xs :: b. Since the function returns xs, the return type is b as well, so the type is:
f :: a -> b -> b
f x xs = xs
ii. f x xs = x+1
(...)
ii) f :: Int -> a -> Int
Again you make the function too restrictive. Let us again assume that x :: a and xs :: b have different types. We see that we return x + 1 (or in more canonical form (+) x 1. Since (+) has signature (+) :: Num c => c -> c -> c (we here use c since a is already used), and 1 has signature 1 :: Num d => d, we thus see that we call (+) with x and 1, as a result we know that a ~ c (a and c are the same type), and c ~ d, so as a result we obtain the signature:
f :: Num c => c -> b -> c
f x xs = x+1
iii. f x xs = x ++ xs
(...)
iii) f :: Eq a => a -> a -> a
This is wrong: we here see that f has two parameters, x :: a and xs :: b. We see that we return (++) x xs. Since (++) has signature (++) :: [c] -> [c] -> [c], we thus know that a ~ [c] and b ~ [c], so the type is:
f :: [c] -> [c] -> [c]
f x xs = x ++ xs
iv. f x (y:ys) = y
(...)
iv) f :: a -> [a] -> a
This is again too restrictive. Here we see again two parameters: x and (y:ys). We first generate a type a for x :: a, and (y:ys) :: b, since the pattern of the second parameter is (y:ys), this is a list constructor with as parameters (:) :: c -> [c] -> [c]. As a result we can derive that y :: c, and ys :: [c], furthermore the pattern (y:ys) has type [c]. Since the function returns y, we know that the return type is c, so:
f :: a -> [c] -> c
f x (y:ys) = y
Note: you can let Haskell derive the type of the function itself. In GHCi you can use the :t command to query the type of an expression. For example:
Prelude> f x (y:ys) = y
Prelude> :t f
f :: t1 -> [t] -> t

Does haskell's foldr always take a two-parameter lambda?

Haskell newb here
I'm working on this problem in haskell:
(**) Eliminate consecutive duplicates of list elements.
If a list contains repeated elements they should be replaced with a single copy of the element. The order of the elements should not be changed.
Example:
* (compress '(a a a a b c c a a d e e e e))
(A B C A D E)
The solution (which I had to look up) uses foldr:
compress' :: (Eq a) => [a] -> [a]
compress' xs = foldr (\x acc -> if x == (head acc) then acc else x:acc) [last xs] xs
This foldr, according to the solution, takes two parameters, x and acc. It would seem like all foldr's take these parameters; is there any exception to this? Like a foldr that takes 3 or more? If not, is this convention redundant and can the formula be written with less code?
foldr takes a function of 2 arguments, but this doesn't prevent it from taking a function of 3 arguments provided that function has the right type signature.
If we had a function
g :: x -> y -> z -> w
With
foldr :: (a -> b -> b) -> b -> [a] -> b
Where we want to pass g to foldr, then (a -> b -> b) ~ (x -> y -> z -> w) (where ~ is type equality). Since -> is right associative, this means we can write g's signature as
x -> y -> (z -> w)
and its meaning is the same. Now we've produced a function of two parameters that returns a function of one parameter. In order to unify this with the type a -> b -> b, we just need to line up the arguments:
a -> | x ->
b -> | y ->
b | (z -> w)
This means that b ~ z -> w, so y ~ b ~ z -> w and a ~ x so g's type really has to be
g :: x -> (z -> w) -> (z -> w)
implying
foldr g :: (z -> w) -> [x] -> (z -> w)
This is certainly not impossible, although more unlikely. Our accumulator is a function instead, and to me this begs to be demonstrated with DiffLists:
type DiffList a = [a] -> [a]
append :: a -> DiffList a -> DiffList a
append x dl = \xs -> dl xs ++ [x]
reverse' :: [a] -> [a]
reverse' xs = foldr append (const []) xs $ []
Note that foldr append (const []) xs returns a function which we apply to [] to reverse a list. In this case we've given an alias to functions of the type [a] -> [a] called DiffList, but it's really no different than having written
append :: a -> ([a] -> [a]) -> [a] -> [a]
which is a function of 3 arguments.
As with all things in haskell have a look at the types of things to guide your way you can do this for any function in ghci.
Looking at this for foldr we see:
Prelude> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
This slightly abstract string can be written in english as:
foldr is a function that takes
1 ) a function with two parameters one of type a and one of type b and returns something of type b
2 ) A value of type b
3 ) A list of values of type a
And returns a value of type b
Where a and b are type variables (see here for a good tutorial on them) which can be filled in with any type you like.
It turns out that you can solve your compress problem using a foldr with a three-argument function.
compress :: Eq a => [a] -> [a]
compress [] = []
compress (z:zs) = z : foldr f (const []) zs z
where f x k w | x==w = k x
| otherwise = x : k x
Let's dissect that. First, we can improve readability by changing the last two lines to
where f x k = \w -> if x==w then k x else x : k x
This makes it evident that a ternary function is nothing but a binary function returning a unary function. The advantage of looking at it in this way is that foldr is best understood when passed a binary function. Indeed, we are passing a binary function, which just happens to return a function.
Let's focus on types now:
f :: a -> (a -> [a]) -> (a -> [a])
f x k
So, x::a is the element of the list we are folding on. Function k is the result of the fold on the list tail. The result of f x k is something having the same type as k.
\w -> if .... :: (a -> [a])
The overall idea behind this anonymous function is as follows. The parameter k plays the same role as acc in the OP code, except it is a function expecting the previous element w in the list before producing the accumulated compressed list.
Concretely, we use now k x when we used acc, passing on the current element to the next step, since by that time x will become the previous element w. At the top-level, we pass z to the function which is returned by foldr f (const []).
This compress variant is lazy, unlike the posted solution. In fact, the posted solution needs to scan the whole list before starting producing something: this is due to (\x acc -> ...) being strict in acc, and to the use of last xs. Instead, the above compress outputs list elements in a "streaming" fashion. Indeed, it works with infinite lists as well:
> take 10 $ compress [1..]
[1,2,3,4,5,6,7,8,9,10]
That being said, I think using a foldr here feels a bit weird: the code above is arguably less readable than the explicit recursion.

Why do i get infinite type error

I am trying to implement my own concat using folds and stumbling here. Why do i get "cannot create infinite types" error?
merge2 xs = foldl conc ([]) xs
where conc x y = (x : y)
Look at the type of x and y in the expression x : y - the type of y must be a list of whatever type x is, due to the definition of :. If you try to follow the types of foldl, in particular the type of its accumulator function, you will end up needing a list of lists of lists of...
Here's how you could trick GHCi into showing you the types of interim expressions. First, your original definition,
f xs = foldl conc [] xs where conc x y = (x : y)
gives you an error about "infinite type" "a = [a]". Now, replace conc in foldl with something else:
f xs = foldl g [] xs where conc x y = (x : y) ; g = g
No type errors there! If you still have errors replace every identifier used in some expression with g2 = g2, g3 = g3 etc. Such definitions are equivalent to defining g = undefined, whose use will cause an error, but more importantly for us its type is free to become anything, as needed.
Now the trick is to define it as some simple value, say a Boolean:
f xs = foldl g [] xs where conc x y = (x : y) ; g = True
this gives us an error about type mismatch between "a -> b -> a" and "Bool". We know True has type Bool, so we conclude that g must be of type a -> b -> a. We could find this out directly as well, with :t command:
Prelude> :t foldl
foldl :: (a -> b -> a) -> a -> [b] -> a
-------------
Continuing, we can now compare conc with some simple value:
f xs = foldl g [] xs where conc x y = (x : y) ; g = g ; h = conc == True
Again we get type mismatch error between "a -> [a] -> [a]" and "Bool". True :: Bool (has type Bool), so conc :: a -> [a] -> [a]. And originally we used it where now g is, so their types must match:
g :: a -> b -> a
conc :: a -> [a] -> [a]
------
a ~ [a]
which is impossible.

Resources