Хаскелл и функциональная композиция


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

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

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

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 будет иметь значение? Может быть, я упустил что-то фундаментальное?

Спасибо

2 2

2 ответа:

Различие вызвано тем, что 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, но вторая половина не будет работать. Отсюда и рвота компилятора.

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

(.) 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-кортеж, поэтому ваша первая версия в порядке.