3

Morte was designed to serve as an intermediate language for super-optimizing functional programs. In order to preserve strong normalization it has no direct recursion, so, inductive types such as lists are represented as folds, and conductive types such as infinite lists are represented as streams:

finiteList :: List Int
finiteList = \cons nil -> cons 0 (cons 1 (cons 2 nil))

infiniteList :: Stream Int
infiniteList = Stream 0 (\n -> (n, n + 1))

I want to rewrite Haskell's enumFromTo on Morte, so that:

enumFromTo 0 2

normalizes to:

\cons nil → cons 0 (cons 1 (cons 2 nil))

Is that possible?

MaiaVictor
  • 51,090
  • 44
  • 144
  • 286

1 Answers1

5

In Morte, a natural number is encoded as a value of type:

forall (Nat : *) -> (Nat -> Nat) -> Nat -> Nat

So, for example, 0, 1, and 2 in Morte would be represented as:

(   \(Nat : *)
->  \(zero : Nat)
->  \(one  : Nat)
->  \(two  : Nat)
->  \(foldNat : Nat -> forall (x : *) -> (x -> x) -> x -> x)
->  ...
)

-- Nat
(forall (Nat : *) -> (Nat -> Nat) -> Nat -> Nat)

-- zero
(\(Nat : *) -> \(Succ : Nat -> Nat) -> \(Zero : Nat) -> Zero)

-- one
(\(Nat : *) -> \(Succ : Nat -> Nat) -> \(Zero : Nat) -> Succ Zero)

-- two
(\(Nat : *) -> \(Succ : Nat -> Nat) -> \(Zero : Nat) -> Succ (Succ Zero))

-- foldNat
(\(n : forall (Nat : *) -> (Nat -> Nat) -> Nat -> Nat) -> n)

With that encoding, then you can start writing simple things like replicate:

-- Assuming you also defined:
-- List : * -> *
-- Cons : forall (a : *) -> a -> List a -> List a
-- Nil  : forall (a : *) -> List a
-- foldList : forall (a : *)
--          -> List a -> forall (x : *) -> (a -> x -> x) -> x -> x

-- replicate : forall (a : *) -> Nat -> a -> List a
replicate =
        \(a : *)
    ->  \(n : Nat)
    ->  \(va : a)
    ->  foldNat n (List a) (\(as : List a) -> Cons a va as) (Nil a)

Doing enumFromTo would be a little more complex but it would still be possible. You will still use foldNat, but your accumulator would be more complex than List Nat. It would be more like (Nat, List Nat) and then you'd extract the second element of the tuple at the end of the fold. That would, of course, require encoding tuples in Morte, too.

That exceeds my ability to hand-write Morte code on the fly, so I will omit that. However, right now I'm working on a medium-level language that compiles to Morte as we speak and it's only a few lines of code away from supporting recursive types (and non-recursive types are ready). You can check it out here:

https://github.com/Gabriel439/Haskell-Annah-Library

Once that code is ready you'd then just be able to write:

type Nat : *
data Succ (pred : Nat) : Nat
data Zero              : Nat
in

type List (a : *) : *
data Cons (head : a) (tail : List a) : List a
data Nil                             : List a
in

let One : Nat = Succ Zero
let Two : Nat = Succ (Succ Zero)
let Three : Nat = Succ (Succ (Succ Zero))
let replicate (a : *) (n : Nat) (va : a) : List a =
        foldNat n (List a) (\(as : List a) -> Cons a va as) (Nil a)
in

replicate Nat Two Three

It's medium-level in the sense that you'd still have to deal with explicitly writing out a fold and figuring out the correct intermediate state to use as the accumulator, but one of the things it simplifies are let and data type declarations. It will also eventually support built-in decimal syntax for Nat, but I haven't started that yet.

Edit: Now annah supports recursive types, and the above annah code normalizes to:

$ annah < replicate.an
∀(List : * → *) → ((∀(Nat : *) → (Nat → Nat) → Nat → Nat) → List (∀(Nat : *) → (Nat → Nat) → Nat → Nat) → List (∀(Nat : *) → (Nat → Nat) → Nat → Nat)) → List (∀(Nat : *) → (Nat → Nat) → Nat → Nat) → List (∀(Nat : *) → (Nat → Nat) → Nat → Nat)

λ(List : * → *) → λ(Cons : (∀(Nat : *) → (Nat → Nat) → Nat → Nat) → List (∀(Nat : *) → (Nat → Nat) → Nat → Nat) → List (∀(Nat : *) → (Nat → Nat) → Nat → Nat)) → λ(Nil : List (∀(Nat : *) → (Nat → Nat) → Nat → Nat)) → Cons (λ(Nat : *) → λ(Succ : Nat → Nat) → λ(Zero : Nat) → Succ (Succ (Succ Zero))) (Cons (λ(Nat : *) → λ(Succ : Nat → Nat) → λ(Zero : Nat) → Succ (Succ (Succ Zero))) Nil)

... which I will format to make it slightly more readable:

    λ(List : * → *)
→   λ(  Cons
    :   (∀(Nat : *) → (Nat → Nat) → Nat → Nat)
    →   List (∀(Nat : *) → (Nat → Nat) → Nat → Nat)
    →   List (∀(Nat : *) → (Nat → Nat) → Nat → Nat)
    )
→   λ(Nil : List (∀(Nat : *) → (Nat → Nat) → Nat → Nat))
→   Cons
        (   λ(Nat : *)
        →   λ(Succ : Nat → Nat)
        →   λ(Zero : Nat)
        →   Succ (Succ (Succ Zero))
        )
        (Cons
            (   λ(Nat : *)
            →   λ(Succ : Nat → Nat)
            →   λ(Zero : Nat)
            →   Succ (Succ (Succ Zero))
            )
            Nil
        )

If you look closely, it produced a list with two elements, each of which is a church-encoded number three.

Gabriella Gonzalez
  • 34,863
  • 3
  • 77
  • 135
  • I didn't think in using numbers like that. Makes total sense. Also, you're the reason the internet is awesome. Thank you. – MaiaVictor Feb 21 '15 at 03:36
  • 1
    @Viclib Also, I was able to get recursive types working for `annah`, and I've updated my answer so that you can see what `replicate Nat Two Three` would get normalized to. – Gabriella Gonzalez Feb 21 '15 at 20:59
  • Great work! I can understand all of this post. There are still some things I can't figure out how to implement... and some doubts... but I will keep exercising my neurons before bothering you. Thanks @Gabriel! – MaiaVictor Feb 21 '15 at 22:10
  • You're welcome! A good starting point is to try to implement it in Haskell first and then translate that to Annah – Gabriella Gonzalez Feb 21 '15 at 22:18