2016-01-12 3 views
2

Я изучал некоторые основные функции композиции в Haskell, и пока я играл, я понял то, что я не могу объяснить. Когда я использую следующий блок кода компилятор, кажется, чтобы быть счастливым об этом, и работает отлично:Haskell и функциональная композиция

doSomeX x = if x==7 then True else False 
doSomeY (x,y) = x+y+1 
doSomeXY = doSomeX.doSomeY 

Однако, когда я разделить doSomeY на 2 арг вместо пары, а именно:

doSomeX x = if x==7 then True else False 
doSomeY x y = x+y+1 
doSomeXY = doSomeX.doSomeY 

Я получаю следующую ошибку:

No instance for (Num a0) arising from a use of `doSomeY' 
The type variable `a0' is ambiguous 
Relevant bindings include 
    doSomeXY :: a0 -> Bool (bound at test.hs:21:1) 
Note: there are several potential instances: 
    instance Integral a => Num (GHC.Real.Ratio a) 
    -- Defined in `GHC.Real' 
    instance Num Integer -- Defined in `GHC.Num' 
    instance Num Double -- Defined in `GHC.Float' 
    ...plus three others 
In the second argument of `(.)', namely `doSomeY' 
In the expression: doSomeX . doSomeY 
In an equation for `doSomeXY': doSomeXY = doSomeX . doSomeY 

Я действительно не понимаю, почему. В обоих случаях тип возврата doSomeY имеет тот же тип, что и аргумент arg функции doSomeX, почему бы ввести тип ввода doSomeY? Я пропустил что-то принципиальное здесь?

Благодаря

+1

Вы видите, почему с эквивалентом 'doSomeY x = \ y -> x + y + 1'? – molbdnilo

ответ

6

Разница обусловлена ​​doSomeY с получением числа в первом случае по сравнению с получением функции во втором случае, когда применяется к одному аргументу.

Учитывая

doSomeX x = if x==7 then True else False 
doSomeY x y = x+y+1 

мы можем сделать вывод типов (это не возможно, наиболее общие типы, но они будут делать для наших целей):

doSomeX :: Int -> Bool 
doSomeY :: Int -> Int -> Int 

Помните, что -> по типу подписями присваивается право, поэтому тип doSomeY эквивалентен

doSomeY :: Int -> (Int -> Int) 

Имея это в виду, рассмотрим тип (.):

(.) :: (b -> c) -> (a -> b) -> a -> c 

Если определить

doSomeXY = doSomeX.doSomeY 

... что эквивалентно (.) doSomeX doSomeY, это означает, что первый аргумент (.) является doSomeX и второй аргумент - doSomeY. Следовательно, тип b -> c (первый аргумент (.)) должен соответствовать типу Int -> Bool (тип doSomeX). Так

b ~ Int 
c ~ Bool 

Следовательно,

(.) doSomeX :: (a -> Int) -> a -> Bool 

Теперь, применяя это doSomeY вызывает ошибку типа. Как уже упоминалось выше,

doSomeY :: Int -> (Int -> Int) 

Так что, когда выводя типа для (.) doSomeX doSomeY компилятор должен объединить a -> Int (первый аргумент (.) doSomeX) с Int -> (Int -> Int) (тип doSomeY). a может быть объединен с Int, но второй тайм не работает. Следовательно, компилятор barfs.

3

(.) определяется как

(.) f g x = f (g x) 

так (f . g) x y = (.) f g x y = f (g x) y, не f (g x y) как вы хотели.

С другой стороны, (f . g) (x,y) = f (g (x,y)), потому что (x,y) является один значения, 2-кортеж, так что ваша первая версия хорошо.

Смежные вопросы