Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell / Miranda: Find the type of the function

Brief: This is a past exam question from a Miranda exam but the syntax is very similar to Haskell.

Question: What is the type of the following expression and what does it do? (The definitions of the functions length and swap are given below).

(foldr (+) 0) . (foldr ((:) . length . (swap (:) [] )) [])

length [] = 0

length (x:xs) = 1 + length xs

swap f x y = f y x

Note:

Please feel free to reply in haskell syntax - sorry about putting using the stars as polytypes but i didn't want to translate it incorrectly into haskell. Basically, if one variable has type * and the other has * it means they can be any type but they must both be the same type. If one has ** then it means that it can but does not need to have the same type as *. I think it corresponds to a,b,c etc in haskell usuage.

My working so far

From the definition of length you can see that it finds the length of a list of anything so this gives

length :: [*] -> num.

From the definition I think swap takes in a function and two parameters and produces the function with the two parameters swapped over, so this gives

swap :: (* -> ** -> ***) -> ** -> [*] -> ***

foldr takes a binary function (like plus) a starting value and list and folds the list from right to left using that function. This gives

foldr :: (* -> ** -> **) -> ** -> [*] -> **)

I know in function composition it is right associative so for example everything to the right of the first dot (.) needs to produce a list because it will be given as an argument to the first foldr.

The foldr function outputs a single value ( the result of folding up the list) so I know that the return type is going to be some sort of polytype and not a list of polytype.

My problem

I'm unsure where to go from here really. I can see that swap needs to take in another argument, so does this partial application imply that the whole thing is a function? I'm quite confused!

like image 795
user1058210 Avatar asked Apr 29 '12 16:04

user1058210


People also ask

How do you find type in Haskell?

If you are using an interactive Haskell prompt (like GHCi) you can type :t <expression> and that will give you the type of an expression. e.g. or e.g.

What is Haskell function type?

Haskell has first-class functions : functions are values just like integers, lists, etc. They can be passed as arguments, assigned names, etc. When we define things in our code: val :: Int val = 6 half_of :: Float -> Float half_of x = x/2. … val is value of type Int , and half_of is a value of type Float -> Float .

What does () mean in Haskell?

() is very often used as the result of something that has no interesting result. For example, an IO action that is supposed to perform some I/O and terminate without producing a result will typically have type IO () .

Where is Haskell function?

Definition on Haskell Where Function. Haskell where is not a function rather it is a keyword that is used to divide the more complex logic or calculation into smaller parts, which makes the logic or calculation easy to understand and handle.


2 Answers

You've already got the answer, I'll just write down the derivation step by step so it's easy to see all at once:

xxf xs = foldr (+) 0 . foldr ((:) . length . flip (:) []) [] $ xs
       = sum         $ foldr ((:) . length . (: []))      []   xs
       = sum         $ foldr (\x -> (:) (length [x]))     []   xs
       = sum         $ foldr (\x r ->    length [x]:r)    []   xs
       = sum         $ map   (\x   ->    length [x]  )         xs
       = sum                            [length [x]  |    x <- xs]  
       = sum                            [ 1          |    x <- xs]
--     = length xs
xxf :: (Num n) => [a] -> n

So that, in Miranda, xxf xs = #xs. I guess its type is :: [*] -> num in Miranda syntax.

Haskell's length is :: [a] -> Int, but as defined here, it is :: (Num n) => [a] -> n because it uses Num's (+) and two literals, 0 and 1.

If you're having trouble visualizing foldr, it is simply

foldr (+) 0 (a:(b:(c:(d:(e:(...:(z:[])...))))))
      =      a+(b+(c+(d+(e+(...+(z+ 0)...)))))
      = sum [a, b, c, d, e, ..., z]
like image 69
Will Ness Avatar answered Oct 13 '22 02:10

Will Ness


Let's go through this step-by-step.

The length function obviously has the type that you described; in Haskell it's Num n => [a] -> n. The equivalent Haskell function is length (It uses Int instead of any Num n).

The swap function takes a function to invoke and reverses its first two arguments. You didn't get the signature quite right; it's (a -> b -> c) -> b -> a -> c. The equivalent Haskell function is flip.

The foldr function has the type that you described; namely (a -> b -> b) -> b -> [a] -> b. The equivalent Haskell function is foldr.

Now, let's see what each sub expression in the main expression means.

The expression swap (:) [] takes the (:) function and swaps its arguments. The (:) function has type a -> [a] -> [a], so swapping it yields [a] -> a -> [a]; the whole expression thus has type a -> [a] because the swapped function is applied to []. What the resulting function does is that it constructs a list of one item given that item.

For simplicity, let's extract that part into a function:

singleton :: a -> [a]
singleton = swap (:) []

Now, the next expression is (:) . length . singleton. The (:) function still has type a -> [a] -> [a]; what the (.) function does is that it composes functions, so if you have a function foo :: a -> ... and a function bar :: b -> a, foo . bar will have type b -> .... The expression (:) . length thus has type Num n => [a] -> [n] -> [n] (Remember that length returns a Num), and the expression (:) . length . singleton has type Num => a -> [n] -> [n]. What the resulting expression does is kind of strange: given any value of type a and some list, it will ignore the a and prepend the number 1 to that list.

For simplicity, let's make a function out of that:

constPrependOne :: Num n => a -> [n] -> [n]
constPrependOne = (:) . length . singleton

You should already be familiar with foldr. It performs a right-fold over a list using a function. In this situation, it calls constPrependOne on each element, so the expression foldr constPrependOne [] just constructs a list of ones with equal length to the input list. So let's make a function out of that:

listOfOnesWithSameLength :: Num n => [a] -> [n]
listOfOnesWithSameLength = foldr constPrependOne []

If you have a list [2, 4, 7, 2, 5], you'll get [1, 1, 1, 1, 1] when applying listOfOnesWithSameLength.

Then, the foldr (+) 0 function is another right-fold. It is equivalent to the sum function in Haskell; it sums the elements of a list.

So, let's make a function:

sum :: Num n => [n] -> n
sum = foldr (+) 0

If you now compose the functions:

func = sum . listOfOnesWithSameLength

... you get the resulting expression. Given some list, it creates a list of equal length consisting of only ones, and then sums the elements of that list. It does in other words behave exactly like length, only using a much slower algorithm. So, the final function is:

inefficientLength :: Num n => [a] -> n
inefficientLength = sum . listOfOnesWithSameLength
like image 34
dflemstr Avatar answered Oct 13 '22 01:10

dflemstr