Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Equivalent functions producing different interpreter results

Background: I'm investigating anonymous recursion, and I'm taking on the challenge of implementing the prelude without using any named recursion just to help it all sit nicely in my mind. I'm not quite there yet, and along the way I've run into something unrelated but still interesting.

map1     = \f -> \x -> if (tail x) == [] 
                       then [f (head x)] 
                       else f (head x) : (map1 f (tail x))

map2 f x =             if (tail x) == [] 
                       then [f (head x)] 
                       else f (head x) : (map2 f (tail x))

map3 f (x:xs) = if xs == [] then [f x] else f x : (map3 f xs)

map4 f (x:[]) = [f x]
map4 f (x:xs) =  f x : map4 f xs

GHC complains about the first one, is fine with the second and the third and fourth ones are there just to show how they could be implemented differently.

*Main> map1 (*2) [1..10]

<interactive>:1:15:
    No instance for (Num ())
      arising from the literal `10'
    Possible fix: add an instance declaration for (Num ())
    In the expression: 10
    In the second argument of `map1', namely `[1 .. 10]'
    In the expression: map1 (* 2) [1 .. 10]
*Main> map2 (*2) [1..10]
[2,4,6,8,10,12,14,16,18,20]
*Main> map3 (*2) [1..10]
[2,4,6,8,10,12,14,16,18,20]
*Main> map4 (*2) [1..10]
[2,4,6,8,10,12,14,16,18,20]

If I add a type signature to map1 it's all good.

map1 :: Eq a => (a -> b) -> [a] -> [b]

The first two functions seem pretty much the same to me, so I suppose my question is simply "What's going on here?"

like image 229
TheIronKnuckle Avatar asked Jan 17 '12 00:01

TheIronKnuckle


People also ask

Can a function in Python work without taking any parameters?

We need function parameters almost always. They're used when we want to pass data into our function. When we call a function we pass arguments (values for the parameters) to the function.

What is definitional interpreter?

A definitional interpreter defines the semantics of an object language in terms of the (well-known) semantics of a host language, enabling understanding and validation of the semantics through execution. Combining a definitional interpreter with a separate type system requires a separate type safety proof.

Which word do you use in Python to define a new function?

Note: The def keyword introduces a new Python function definition.

How to define a function that takes an argument in Python?

Arguments are specified after the function name, inside the parentheses. You can add as many arguments as you want, just separate them with a comma.


2 Answers

You were bitten by the monomorphism restriction. Anything that is written as foo = ... - meaning that there are no arguments to the definition, and no explicit type is given - has to have a non-generic type according to this restriction. The generic type in this case would, as you said, have had to be Eq a => (a -> b) -> [a] -> [b], but since the monomorphism restriction applies, both a and b are replaced by (), the simplest type that can be inferred for the available type variables.

like image 126
dflemstr Avatar answered Sep 27 '22 22:09

dflemstr


But, if you use the unconstrained null instead of (== []),

Prelude> let map0 = \f -> \x -> if null (tail x) then [f (head x)] else f (head x) : map0 f (tail x)
Prelude> :t map0
map0 :: (a -> t) -> [a] -> [t]
Prelude> map (*2) [1 .. 10]
[2,4,6,8,10,12,14,16,18,20]

it works without arguments or a signature. Only constrained type variables are subject to the monomorphism restriction.

And if you put the definition of map1 into a file and try to compile it or load it into ghci,

Ambiguous type variable `a0' in the constraint:
  (Eq a0) arising from a use of `=='
Possible cause: the monomorphism restriction applied to the following:
  map1 :: forall t. (a0 -> t) -> [a0] -> [t] (bound at Maps.hs:3:1)
Probable fix: give these definition(s) an explicit type signature
              or use -XNoMonomorphismRestriction
In the expression: (tail x) == []
In the expression:
  if (tail x) == [] then
      [f (head x)]
  else
      f (head x) : (map1 f (tail x))
In the expression:
  \ x
    -> if (tail x) == [] then
           [f (head x)]
       else
           f (head x) : (map1 f (tail x))

ghci only complained in the way it did because it uses ExtendedDefaultRules, otherwise you would have gotten an understandable error message.

like image 23
Daniel Fischer Avatar answered Sep 28 '22 00:09

Daniel Fischer