Это всегда хорошо, чтобы подчеркнуть, что Haskell имеет два совершенно разных пространств имен, в типа языка и язык значение. В вашем случае есть
- Тип конструктора
C :: * -> * -> *
, который живет на языке типа. Требуется два типа: a
, b
(вида *
) и сопоставляет их с типом C a b
(также вида *
).
- Конструктор значений
C :: ((a->b) -> a) -> C a b
, который живет на языке значений. Требуется функция f
(типа (a->b) -> a
) и сопоставляет ее со значением C f
(типа C a b
).
Возможно, было бы менее запутанным, если у вас
newtype CT a b = CV ((a->b) -> a)
а потому, что для Newtype всегда точно один конструктор значение (а именно один конструктор типа), имеет смысл называть их так же.
CV
- конструктор значений, который принимает одну функцию, полную остановку. Эта функция будет иметь подпись (a->b) -> a
, т. Е. Ее аргумент также является функцией, но в отношении CT
это не имеет большого значения.
Действительно, это отчасти неправильно, что data
и newtype
декларации использовать =
символ, потому что это не означает, что вещи на правой и левой “ то же ” – не может, потому что они даже не принадлежат на тот же язык. Там это альтернативный синтаксис, который выражает отношение лучше:
{-# LANGUAGE GADTs #-}
data CT :: * -> * -> * where
CV :: ((a->b) -> a) -> CT a b
Что касается этого значения вы пытались построить
let d1 = CV $ (\x->(2*x)) 3
здесь вы сделали не пас “ функцию и параметр ” - CV
. Что вы на самом деле сделали был, вы применили функцию \x->2*x
к значению 3
(возможно, также написали 6
) и передали этот номер CV
. Но, как я уже сказал, CV
ожидает функцию. Что тогда происходит, GHC пытается интерпретировать 6
как функцию, которая дает фиктивное ограничение Num ((a->b) -> a)
. Что это означает: “, если (a->b)->a
- это номер, то ... ”. Конечно это не a номер type, so the rest doesn't make sense either.
Это потому, что $
имеет самый низкий приоритет, то есть выражение CV $ (\x->(2*x)) 3
фактически анализируется как CV ((\x->(2*x)) 3)
, или, что эквивалентно let y = 2*3 in CV y
.
Так как же это объяснить, А Haskell продолжение имеет следующий вид: NewType Конт ра = Cont {runCont :: (а -> г) -> г} Продолжение принимает функцию типа (a -> r) и порождает r, где r иногда может быть фиксированным значением, таким как Int или IO(). – coffeequant
Это означает: _A продолжение Haskell 'c' имеет тип следующего вида:' c :: (A -> R) -> R', например, это может быть 'c :: (String -> Int) - > Int'. Чтобы абстрагироваться от этого, мы определяем оболочку 'newtype Cont ra = ...', так что тогда мы можем просто написать 'c ':: Cont String Int', определяемый' c' = Cont c'._ – leftaroundabout
Ok let's следуйте с вашим определением, что ((a-> b) -> a) - это функция, которая требуется в конструкторе значений C. Как мне теперь инициализировать C? Скажем, я определяю функцию let f a = 2 * a, которая может действовать как параметр для конструктора значений C. Я немного потерял с определением C, используя эту функцию f. Если это C (\ x e -> x e), где x - функция, а e - параметр, и я вызываю функцию по этому параметру e. – coffeequant