вопрос хаскельщикам
Apr. 20th, 2018 06:35 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
true x y = x false x y = y lampair :: a -> b -> (a -> b -> c) -> c lampair x y p = p x y pa :: ((a -> b -> c) ->c) -> a pa p = p true
And I see
• Couldn't match type ‘a’ with ‘c’ ‘a’ is a rigid type variable bound by the type signature for: pa :: forall a b c. ((a -> b -> c) -> c) -> a at sample1.hs:8:1-30 ‘c’ is a rigid type variable bound by the type signature for: pa :: forall a b c. ((a -> b -> c) -> c) -> a at sample1.hs:8:1-30 Expected type: a -> b -> c Actual type: c -> b -> c • In the first argument of ‘p’, namely ‘true’ In the expression: p true In an equation for ‘pa’: pa p = p true • Relevant bindings include p :: (a -> b -> c) -> c (bound at sample1.hs:9:4) pa :: ((a -> b -> c) -> c) -> a (bound at sample1.hs:9:1) |
What have I done wrong? What fails here? Is Hindley-Milner "not smart enough"? What am I missing?
no subject
Date: 2018-04-21 03:14 am (UTC)Например, предположим мы имеем
foo :: (Int -> String -> Bool) -> Bool
и вызываем p foo, очевидно, что foo не может принимать true.
Если c поменять на a, то всё сойдётся, не знаю, нужно ли это. Что предполагается получить?
no subject
Date: 2018-04-21 03:51 am (UTC)Вот, например, скала:
Уж не в том ли проблема, что типы Черча в некотором смысле не столь, э, сильны, как типы Карри?
no subject
Date: 2018-04-21 04:45 am (UTC)Например, если мы переведём её опять в контравариантную позицию, то можно:
no subject
Date: 2018-04-21 02:27 pm (UTC)no subject
Date: 2018-04-21 04:53 am (UTC)Контравариантная у нас super, а ковариантная extends. В терминах Java. В терминах Scala - и +.
Обратный пример тоже на всякий случай покажу. Вот это компилируется:
Типы поменялись местами по сравнению с первым примером (у нас принимает более общий тип в том месте, где мы ожидаем более конкретный. Хотя я предпочитаю так не рассуждать, а просто пробежаться по стрелочками и посчитать в какой позиции аргумент.
no subject
Date: 2018-04-21 05:16 am (UTC)Вот что читаю: https://ifl2014.github.io/submissions/ifl2014_submission_13.pdf - и поразило, что swap нельзя так просто написать.
Но вот так работает:
no subject
Date: 2018-04-21 05:50 am (UTC)Статью потом прочту, уже час ночи, спать пора.
no subject
Date: 2018-04-21 02:28 pm (UTC)no subject
Date: 2018-04-21 04:46 pm (UTC)That is, if you don't use lambdas, you get your code, but with the corrected types :)
pa :: ((a->b->a)->c)->c
no subject
Date: 2018-04-21 08:31 am (UTC)no subject
Date: 2018-04-21 01:43 pm (UTC)A->B->A (specific types) is a special case of a->b->c (type variables) in some sense.
Where did you get a->b->a (type variables) being a special case of a->b->c?
no subject
Date: 2018-04-21 02:26 pm (UTC)no subject
Date: 2018-04-21 03:20 pm (UTC)One of the problems is that there is a type a->b->c, but there is no function that can return a value of type c for any c.
a->b->c (type variables) is a family of functions.
a->b->a (type variables) is a family of functions included in the family of functions a->b->c.
You can pass any function from family a->b->a into a context expecting a->b->c, where at least c is not bound - which is to say, you can use any function from family a->b->a only where all functions a->b->c are allowed.
In pa :: ((a -> b -> c) ->c) -> a not all functions a->b->c are allowed.
no subject
Date: 2018-04-21 03:39 pm (UTC)"Every ten minutes a man is mugged in the streets of New York. Now we are interviewing this man."
no subject
Date: 2018-04-21 03:44 pm (UTC)So you can't return type a, you've got to return at most something of type c - as long as you can pass a function of type a->b->c for given a, b and c to a given function p. You can't even construct such a (terminating) function that can (construct and) pass such matching functions to p.
"p true" not matching the type is a minor problem compared to the two arguments above.
no subject
Date: 2018-04-21 08:15 am (UTC)Then specifying that argument is of type (a->b->c)->c means the argument is allowed to be bound to _any_ three types, so true does not fit, because it forces the return type to be the same as the first argument.
You probably can get away with nesting the predicate - to express that the function p should be polymorphic of a and b.
...and you can! (ghci -XRankNTypes)
no subject
Date: 2018-04-21 08:48 am (UTC)(Also, could think of how polymorphism / typeclasses work: you've got to pass the pointer to instance / implementation table)
(this is equivalent to your first declaration)
is obviously different from
(is not meant to work as a type for pa p = p true, just an example)
different from
(the actual thing that works)
and different from
You see, when you declare the types are on input, it means that the call site literally can "choose" any values for types, and it will work.
So in your example (the first declaration here) the caller can choose any A, B and C, and yet you pass a specific instance true, which is not polymorphic in return type - it binds C to be the same as A. You did not express this to be the case, so the caller of pa cannot tell that A and C must be bound in some specific way. (This reference to a "caller" is a rather mechanical way of explaining; something like the intuition behind it. I am sure type theory has the actual explanation.)
Similarly in the third case you tell the caller that pa decides what the types A and B are, so the caller of pa must pass a p that can accept any A and B.
The fourth case is different yet again - you tell the caller that p can decide what the types A and B are, whereas C is still determined by the caller directly, so pa will make sure it will pass such a function that works. (true does not match the description, because it is (A B : Set) -> A -> B -> A, not (A B C : Set) -> A -> B -> C - it does not produce a value of any given type C)
Also, once you write the types down like this, it is obvious that even though the types join up in the third case, there is no way a function p can be instantiated, except a const, perhaps - there is no way p can construct a value for any A and B it is given by pa.
no subject
Date: 2018-04-21 06:28 pm (UTC)no subject
Date: 2018-04-21 08:40 pm (UTC)no subject
Date: 2018-04-23 08:50 am (UTC)- I'm surprised this surprises you.
- What sassa_nf is saying above is very much to the point.
- I honestly think you're going backwards about this when you're talking about doing this "категорно". Just start out with proper Λ/Π and no :>, everything is gonna make sense. Once it does, I guess you could always do this "категорно", although I wouldn't know anything about that, obviously...
no subject
Date: 2018-04-23 02:01 pm (UTC)Regarding expressing stuff categorically, it's a very interesting question I'm thinking about now. But rather in the opposite direction, categories in lambda.