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