RankNTypes and Church numerals - haskell

I'm trying to study church numerals in Haskell by giving the numbers a type like this, with the idea that a natural number n is basically the expression that applies the function in the following type to the value of type t for n times.
type Nat = forall t. (t -> t) -> t -> t
With that idea, I can define zero, successor, plus, mult in the following ways:
zero :: Nat
zero = \f t -> t
succ :: Nat -> Nat
succ n = \f -> f . (n f)
plus :: Nat -> Nat -> Nat
plus m n = \f -> (m f) . (n f)
mult :: Nat -> Nat -> Nat
mult m n = \f -> m (n f) -- Equal to \f -> (m . n) f
-- Pointfree version
mult' :: Nat -> Nat -> Nat
mult' = (.)
When I try to define exponentiation, I'd like to try applying the same reasoning that allowed me to define multiplication, namely applying mult m n times to 1.
This leads to the following code
exp' :: Nat -> Nat -> Nat
exp' m n = n (mult m) (succ zero)
But this does type check with the following error from GHC:
Couldn't match type ‘t’ with ‘t1’
‘t’ is a rigid type variable bound by
the type signature for:
exp' :: forall t. Nat -> Nat -> (t -> t) -> t -> t
at church.lhs:44:3
‘t1’ is a rigid type variable bound by
a type expected by the context:
forall t1. (t1 -> t1) -> t1 -> t1
at church.lhs:44:17
Expected type: ((t -> t) -> t -> t) -> (t -> t) -> t -> t
Actual type: Nat -> Nat
The error seems to say that the typechecker is not instantiating the type for n properly, ideally type t should be instantiated with another (t -> t) for the expression to go through.
What's also confusing me is that the following code typechecks:
exp :: Nat -> Nat -> Nat
exp m n = n ((.) m) (succ zero) -- replace mult by (.)
Would someone mind explaining what's the problem here? Why does the first definition of exp' not typecheck but the second exp typecheks?
Thanks!

The reason that it doesn't work is it involves several impredicative instantiations, which isn't even normally allowed in Haskell. If you turn on -XImpredicativeTypes, you can get it to compile:
{-# LANGUAGE ImpredicativeTypes #-}
...
exp' :: Nat -> Nat -> Nat
exp' m n = n (mult m) (succ zero)
The second version typechecks because mult' has a higher rank type, even though it is definitionally equal to (.), so typechecking proceeds differently. Since the type of (.) is simpler (rank 1) typechecking will succeed more often.
The GHC docs warn ImpredicativeTypes does not work so I would caution against using it. The typical way to get around this it to simple use a newtype:
newtype Nat' = N { instN :: Nat }
exp'' :: Nat -> Nat -> Nat
exp'' m n = instN $ n (\(N q) -> N $ mult m q) (N $ succC zero)
To see the impredicative instantiation in action, you can use typed holes:
exp' :: Nat -> Nat -> Nat
exp' m n = _ (mult m) (succC zero)
This will report the type forall a . (Nat -> Nat) -> Nat -> (a -> a) -> a -> a, which is the same as (Nat -> Nat) -> Nat -> Nat. Since you place n there, you have to unify this type with forall a . (a -> a) -> a -> a, which involves instantiating the type variable a with the polytype Nat.

Related

"case" operator for System-F natural numbers coded with RankNTypes fails to typecheck

In Haskell, if one enables the RankNTypes extension
{-# Language RankNTypes #-}
then one can define the natural numbers as they are encoded in System-F:
type Nat = forall a. a -> ((a -> a) -> a)
zero :: Nat
zero = \z s -> z
succ :: Nat -> Nat
succ n = \z s -> s (n z s)
fold :: a -> (a -> a) -> Nat -> a
fold z s n = n z s
Yay! The next step is to define the case operation: the idea is that
caseN :: Nat -> a -> (Nat -> a) -> a
caseN n z f = "case n of
zero -> z
succ m -> f m"
Of course that's not directly possible. One thing that is possible is to define the natural numbers as normally {data Nats = Zero | Succ Nats} and define "conversions" between Nat and Nats, and then use the syntactic case construct built-in to Haskell.
In the untyped lambda calculus, caseN can be written as
caseN n b f = snd (fold (zero, b) (\(n0, _) -> (succ n0, f n0)) n)
following a trick apparently discovered by Kleene for defining the predecessor function. This version of caseN does look like it should typecheck with the type given above. (zero, b) :: (Nat, b) and \(n0, _) -> (succ n0, f n0) :: (Nat, b) -> (Nat, b), so fold (zero, b) (\(n0, _) -> (succ n0, f n0)) n :: (Nat, b).
However this doesn't typecheck in Haskell. Trying to isolate the inner function \(n0, _) -> (succ n0, f n0) with
succf :: (Nat -> b) -> (Nat, b) -> (Nat, b)
succf f (n, _y) = (succ n, f n)
reveals that the ImpredicativeTypes extension may be needed, as succf seems to require that extension. For the more typical {data Nats = Zero | Succ Nats}, the caseN construct does work (after changing to the appropriate fold, and Zero, Succ).
Is it possible to get caseN to work on Nat directly? Is a different trick needed?
I think the typical trick is to use a data type (or newtype, as pointed out by a commenter) wrapper. To start, instead of defining Nat as a type synonym, you can define it as:
newtype Nat = Nat { unNat :: forall a. a -> ((a -> a) -> a) }
This is isomorphic to your definition, except that you must explicitly wrap and unwrap the contents.
We can continue by writing the same definitions you had:
zero :: Nat
zero = Nat $ \z s -> z
succ :: Nat -> Nat
succ (Nat n) = Nat $ \z s -> s (n z s)
fold :: a -> (a -> a) -> Nat -> a
fold z s (Nat n) = n z s
This is basically what you already had, but now with explicit wrapping and unwrapping using Nat (as both constructor and pattern).
At this point, your final definitions just work:
caseN :: Nat -> b -> (Nat -> b) -> b
caseN n b f = snd (fold (zero,b) (\(n0,_) -> (succ n0,f n0)) n)
succf :: (Nat -> b) -> (Nat, b) -> (Nat, b)
succf f (n,_y) = (succ n, f n)

Is it possible to define variadic-kinded data types?

I can define a polykinded natural transformation like so:
type family (~>) :: k -> k -> *
type instance (~>) = (->)
newtype NT a b = NT { apply :: forall x. a x ~> b x }
type instance (~>) = NT
Which works at all kinds, so I can define e.g.
left :: Either ~> (,)
left = NT (NT (Left . fst))
This is cool and inspiring. But no matter how many tricks I play, I can't seem to get something variadic in the return type. E.g. I would like
type family (:*:) :: k -> k -> k
type instance (:*:) = (,)
type instance (:*:) = ???
It seems like this is impossible, since type families need to be fully saturated, and you can only introduce type constructors in *.
I've even tried some rather nasty tricks
type instance (:*:) = Promote2 (:*:)
type family Promote2 :: (j -> k -> l) -> (a -> j) -> (a -> k) -> (a -> l) where
promote2_law :: Promote2 f x y z :~: f (x z) (y z)
promote2_law = unsafeCoerce Refl
fstP :: forall (a :: k -> *) (b :: k -> *) (c :: k). (a :*: b) c -> a c
fstP = case promote2_law #(:~:) #a #b #c of Refl -> NT (\(a,b) -> a)
And I don't know if that even has any hope of working, since I haven't thought through how higher kinded things are "represented". But GHC knows I'm lying anyway
• Couldn't match type ‘(,)’ with ‘Promote2 (,) a’
Inaccessible code in
a pattern with constructor: Refl :: forall k (a :: k). a :~: a,
Are there any other tricks for this?
The "axiomatic" approach does actually work, I had just used the equality wrong:
fstP :: forall (a :: j -> k) (b :: j -> k) (x :: j). (a :*: b) x -> a x
fstP = castWith (Refl ~% promote2_law #(:*:) #a #b #x ~% Refl) fst
where
infixl 9 ~%
(~%) = Data.Type.Equality.apply
Using Equality.apply is essential to inform the type checker of where to apply the axiom. I made a full development of higher-kinded products here for reference.
Be warned, as I was playing with this did I get a GHC panic once. So the nasty tricks might be nasty. Still interested in other approaches.

How to use the dependent pair type Sigma from the singletons library?

How can the dependent pair type Sigma from the singletons library be used?
Let's assume the following type-indexed list and replicate function exist:
data Vect :: Nat -> Type -> Type where
VNil :: Vect 0 a
VCons :: a -> Vect n a -> Vect (n + 1) a
replicateVec :: forall n a. Sing n -> a -> Vect n a
(You can find a couple different implementations of replicateVec in this question).
I'd like to create a function replicateVecSigma that returns a Vect in a dependent pair. Ideally it would be like the following:
replicateVecSigma :: Natural -> Sigma Nat (\n -> Vect n String)
How can Sigma be used to write this function? And how can the type of the function be written?
Actually, I am able to implement replicateVecSigma as follows but it doesn't seem very clean:
data Foo a b (m :: TyFun Nat Type)
type instance Apply (Foo a b) (n :: Nat) = a n b
replicateVecSigma :: Natural -> Sigma Nat (Foo Vect String)
replicateVecSigma i =
case toSing i of
SomeSing snat -> snat :&: replicateVec snat "hello"
It seems unfortunate that I have to declare this Foo type and Apply instance just to use Sigma. Does the singletons library provide any way to make working with Sigma easier?
You can find my full code here.
For completeness, here is the definition of Sigma:
data Sigma (s :: Type) :: (s ~> Type) -> Type where
(:&:) :: forall s t fst. Sing (fst :: s) -> t ## fst -> Sigma s t
Here is ~>:
type a ~> b = TyFun a b -> *
data TyFun :: * -> * -> *
instance (SingKind k1, SingKind k2) => SingKind (k1 ~> k2)
type instance Demote (k1 ~> k2) = Demote k1 -> Demote k2
class SingKind k where
type family Demote k :: * = r | r -> k
fromSing :: forall (a :: k). Sing a -> Demote k
toSing :: Demote k -> SomeSing k
Here is ##:
type a ## b = Apply a b
type family Apply (f :: k1 ~> k2) (x :: k1) :: k2
singletons defines a bunch of instances for Apply.
How do TyFun and Apply work?
Here are the three questions from above:
How can I use Sigma to write a function like replicateVecSigma :: Natural -> Sigma Nat (\n -> Vect n String)?
I am able to write replicateVecSigma using the Foo type above, but it seems like too much extra work. Is there an easier way?
How do TyFun and Apply work?
Defunctionalization is a general way to work around the lack of first-class functions (in fact, it was originally discovered as a compilation technique to get rid of those), encoding them as symbols that get interpreted by a single first-order Apply function.
For example, your Foo a b is a symbol that encodes the function \n -> a n b.
Another way of obtaining an equivalent symbol is to follow the intuition that (\n -> a n b) = flip a b.
Note that type constructors like Vec are not themselves symbols that can be passed to Apply, but must be wrapped in "symbol constructors": TyCon2 Vec is the symbol for \n -> \b -> Vec n b.
singletons has a symbol for flip, FlipSym0 (symbols can stand for higher-order functions by taking other symbols as arguments).
Foo a b = Apply (Apply FlipSym0 (TyCon2 a)) b
Note that partial applications reduce to other symbols:
= Apply (FlipSym1 (TyCon2 a)) b
= FlipSym2 (TyCon2 a) b
The TyFun type is a way to give types to defunctionalized symbols. The main benefit I see is type inference; without it, type families might get stuck in confusing ways.
See also this blogpost by Richard Eisenberg on defunctionalization for Haskell types: https://typesandkinds.wordpress.com/2013/04/01/defunctionalization-for-the-win/
In this specific case, we want to compute the type-level lambda
\n -> Vect n String
Unfortunately, we do not have lambdas at the type level. At most, we can define type families, as the OP did. However, we can rewrite the lambda in pointfree style:
\n -> flip Vect String n -- pseudocode
flip Vect String
and we can turn this idea into an actual type, using the singletons type-level Flip type function. Here, we want to partially apply Flip with two arguments (it takes three, in a saturated call), so we use the "defunctionalized" FlipSym2 variant instead.
This compiles:
replicateVecSigma :: Natural -> Sigma Nat (FlipSym2 (TyCon Vect) String)
replicateVecSigma i =
case toSing i of
SomeSing snat -> snat :&: replicateVec snat "hello"
If we had the n argument in the last position from the beginning, we could have avoided the Flip.
data Vect2 :: Type -> Nat -> Type where
VNil2 :: Vect2 a 0
VCons2 :: a -> Vect2 a n -> Vect2 a (n + 1)
replicateVec2 :: forall n a. Sing n -> a -> Vect2 a n
replicateVec2 = undefined
replicateVecSigma2 :: Natural -> Sigma Nat (TyCon (Vect2 String))
replicateVecSigma2 i =
case toSing i of
SomeSing snat -> snat :&: replicateVec2 snat "hello"
( TyCon Vect2 ## String also works here, using the explicit application ## at the defunctionalized level, instead of the actuall type applciation.)
Very roughly put, you can think of the defunctionalized variants like FlipSym0, FlipSym1, FlipSym2 as basic tags (not functions!), with no intrinsic meaning, except for the fact that Apply lets you work with them pretending they were functions. In this way we can work with non-functions ("tags") as if they were functions. This is needed since, in Haskell, we do not have functions at the type level, so we have to work with these "pretend" versions.
The approach is general, but it does require more work from the programmer than one would like.
I am not aware of any Template Haskell utility that can automatically turn type-level lambdas into pointfree form, and then defunctionalize them. That would be quite useful.

How to work with higher rank types

Playing with the church numerals. I run into the situation I can't guide the GHC type-checker around higher order types.
First I wrote an version, without any type signatures:
module ChurchStripped where
zero z _ = z
inc n z s = s (n z s)
natInteger n = n 0 (1+)
add a b = a b inc
{-
*ChurchStripped> natInteger $ add (inc $ inc zero) (inc $ inc $ inc zero)
5
-}
mult a b = a zero (add b)
{-
*ChurchStripped> natInteger $ mult (inc $ inc zero) (inc $ inc $ inc zero)
6
-}
The inferred type of mult is horrible, so I tried to clean-up the types with a type definitions:
module Church where
type Nat a = a -> (a -> a) -> a
zero :: Nat a
zero z _ = z
inc :: Nat a -> Nat a
inc n z s = s (n z s)
natInteger :: Nat Integer -> Integer
natInteger n = n 0 (1+)
{- `add :: Nat a -> Nat a -> Nat a` doesn't work, and working signature looks already suspicious -}
add :: Nat (Nat a) -> Nat a -> Nat a
add a b = a b inc
{-
*Church> natInteger $ add (inc $ inc zero) (inc $ inc $ inc zero)
5
-}
mult :: Nat (Nat a) -> Nat (Nat a) -> Nat a
mult a b = a zero (add b)
{-
*Church> natInteger $ mult (inc $ inc zero) (inc $ inc $ inc zero)
6
-}
It works, but the types aren't as clean as they could be. Following the System F definitions I tried:
{-# LANGUAGE RankNTypes #-}
module SystemF where
type Nat = forall a. a -> (a -> a) -> a
zero :: Nat
zero z _ = z
inc :: Nat -> Nat
inc n z s = s (n z s)
natInteger :: Nat -> Integer
natInteger n = n 0 (1+)
{- This doesn't work anymore
add :: Nat -> Nat -> Nat
add a b = a b inc
Couldn't match type `forall a1. a1 -> (a1 -> a1) -> a1'
with `a -> (a -> a) -> a'
Expected type: (a -> (a -> a) -> a) -> a -> (a -> a) -> a
Actual type: Nat -> a -> (a -> a) -> a
In the second argument of `a', namely `inc'
In the expression: a b inc
In an equation for `add': add a b = a b inc
-}
I guess it should be possible to write add with Nat -> Nat -> Nat type signature, but I have no idea how.
P.S. actually I started from the bottom, but it maybe easier to present this problem this way.
bennofs is right, you really want to help the typechecker along here, in particular in add where you need to instantiate the a in forall a . a -> (a -> a) -> a with Nat (ie, the same forall a . ... type).
One way to do that is to introduce a newtype that wraps the polymorphic type:
newtype Nat' = N Nat
Now you can go between Nat and Nat' via N, and back using unN
unN :: Nat' -> Nat
unN (N n) = n
(It's worth noting at this point that newtype Nat' = N Nat is a different beast from data Nat2 = forall a . N2 (a -> (a -> a) -> a). The latter requires -XExistentialQuantification because it says that for some particular choice of a, you can make a Nat2. On the other hand, the former still says that if you have a -> (a -> a) -> a for any arbitrary a, then you can make a Nat'. For Nat' you need -XRankNTypes, but you don't need existentials.)
Now we can also make inc' to increment a Nat':
inc' :: Nat' -> Nat'
inc' (N n) = N (inc n)
And we're ready to add:
add :: Nat -> Nat -> Nat
add n m = unN (n (N m) inc')
The reason this works is because now instead of trying to convince GHC to instantiate the type of n with a polymorphic type ∀ a . a -> (a -> a) -> a on its own, the N acts as a hint.
Example:
> natInteger (add (inc zero) (inc zero))
2
I don't understand RankNTypes well enough to tell why your original example doesn't work, but if you pack Nat into a data type, then it works:
{-# LANGUAGE RankNTypes #-}
module SystemF where
data Nat = Nat (forall a. (a -> (a -> a) -> a))
zero :: Nat
zero = Nat const
inc :: Nat -> Nat
inc (Nat n) = Nat $ \z s -> s $ n z s
natInteger :: Nat -> Integer
natInteger (Nat n) = n 0 (1+)
add :: Nat -> Nat -> Nat
add (Nat a) b = a b inc
Now the Nat type is a real data type, which helps the type checker, because it doesn't have to deal with the polymorphic type all the time, only when you actually "unpack" it.
Here's my implementation of Church numerals:
type Nat = forall a . (a→a) → (a→a)
zero :: Nat
zero _ = id
one :: Nat
one = id
inc :: Nat → Nat
inc a f = f . a f
add :: Nat → Nat → Nat
add a b f = (a f) . (b f)
mul :: Nat → Nat → Nat
mul a b = a . b
nat :: Integer → Nat
nat 0 = zero
nat n = inc $ nat (n-1)
unnat :: Nat → Integer
unnat f = f (+ 1) 0
It is much easier to work with them flipped (function to apply N times first, its argument second). Everything just comes out, well, naturally.
EDIT: this solution is also limited, the types are not right just like in the original question and it will break down at some point.
It seems that by using Data.Proxy we can give a hint to GHC:
{-# LANGUAGE RankNTypes #-}
import Data.Proxy
type Nat = forall a. Proxy a -> (a -> a) -> (a -> a)
zero :: Nat
zero _ _ x = x
suc :: Nat -> Nat
suc n proxy s z = n proxy s (s z)
-- add = \m n f x. m f (n f x)
add :: Nat -> Nat -> Nat
add n m proxy s z = n proxy s (m proxy s z)
-- mult = \m n f. m (n f)
mult :: Nat -> Nat -> Nat
mult m n proxy s z = m proxy (n proxy s) z
And then it works!
λ > :t let one = suc zero in add one one
let one = suc zero in add one one :: Proxy a -> (a -> a) -> a -> a
λ > let one = suc zero in add one one Proxy (1+) 0
2
λ > :t let two = suc (suc zero) in mult two two
let two = suc (suc zero) in mult two two
:: Proxy a -> (a -> a) -> a -> a
λ > let two = suc (suc zero) in mult two two Proxy (1+) 0
4

Lambda calculus in Haskell: Is there some way to make Church numerals type check?

I'm playing with some lambda calculus stuff in Haskell, specifically church numerals. I have the following defined:
let zero = (\f z -> z)
let one = (\f z -> f z)
let two = (\f z -> f (f z))
let iszero = (\n -> n (\x -> (\x y -> y)) (\x y -> x))
let mult = (\m n -> (\s z -> m (\x -> n s x) z))
This works:
:t (\n -> (iszero n) one (mult one one))
This fails with an occurs check:
:t (\n -> (iszero n) one (mult n one))
I have played with iszero and mult with my constants and they seem to be correct. Is there some way to make this typeable? I didn't think what I was doing was too crazy, but maybe I'm doing something wrong?
Your definitions are correct, as are their types, when seen at top-level. The problem is that, in the second example, you're using n in two different ways that don't have the same type--or rather, their types can't be unified, and attempting to do so would produce an infinite type. Similar uses of one work correctly because each use is independently specialized to different types.
To make this work in a straightforward way you need higher-rank polymorphism. The correct type for a church numeral is (forall a. (a -> a) -> a -> a), but higher-rank types can't be inferred, and require a GHC extension such as RankNTypes. If you enable an appropriate extension (you only need rank-2 in this case, I think) and give explicit types for your definitions, they should work without changing the actual implementation.
Note that there are (or at least were) some restrictions on the use of higher-rank polymorphic types. You can, however, wrap your church numerals in something like newtype ChurchNum = ChurchNum (forall a. (a -> a) -> a -> a), which would allow giving them a Num instance as well.
Let's add some type signatures:
type Nat a = (a -> a) -> a -> a
zero :: Nat a
zero = (\f z -> z)
one :: Nat a
one = (\f z -> f z)
two :: Nat a
two = (\f z -> f (f z))
iszero :: Nat (a -> a -> a) -> a -> a -> a
iszero = (\n -> n (\x -> (\x y -> y)) (\x y -> x))
mult :: Nat a -> Nat a -> Nat a
mult = (\m n -> (\s z -> m (\x -> n s x) z))
As you can see, everything seems pretty normal except for the type of iszero.
Let's see what happens with your expression:
*Main> :t (\n -> (iszero n) one n)
<interactive>:1:23:
Occurs check: cannot construct the infinite type:
a0
=
((a0 -> a0) -> a0 -> a0)
-> ((a0 -> a0) -> a0 -> a0) -> (a0 -> a0) -> a0 -> a0
Expected type: Nat a0
Actual type: Nat (Nat a0 -> Nat a0 -> Nat a0)
In the third argument of `iszero', namely `(mult n one)'
In the expression: (iszero n) one (mult n one)
See how the error uses our Nat alias!
We can actually get a similar error with the simpler expression \n -> (iszero n) one n. Here's what's wrong. Since we are calling iszero n, we must have n :: Nat (b -> b -> b). Also, because of iszeros type the second and third arguments, n and one, must have the type b. Now we have two equations for the type of n:
n :: Nat (b -> b -> b)
n :: b
Which can't be reconciled. Bummer.

Resources