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