198 lines
5.5 KiB
Agda
198 lines
5.5 KiB
Agda
|
||
|
||
/-
|
||
Ok, so this is newt, a dependent typed programming language that
|
||
I am implementing to learn how they work. It targets javascript
|
||
and borrows a lot of syntax from Idris and Agda.
|
||
|
||
This page is a very simple web playground based on the monaco editor.
|
||
It runs newt, compiled by Idris2, in a web worker.
|
||
|
||
Block comments follow Lean because they're easier to type on a
|
||
US keyboard.
|
||
|
||
The output, to the right, is somewhat noisy and obtuse. You'll see
|
||
INFO and sometimes ERROR messages that show up in the editor view
|
||
on hover. I'm emitting INFO for solved metas.
|
||
|
||
The Day1.newt and Day2.newt are last year's advent of code, translated
|
||
from Lean.
|
||
|
||
-/
|
||
|
||
-- One-line comments begin with two hyphens
|
||
|
||
-- every file begins with a `module` declaration
|
||
-- it must match the filename
|
||
module Tour
|
||
|
||
-- We can import other modules, with a flat namespace and no cycles,
|
||
-- diamonds are ok
|
||
|
||
-- commented out until we preload other files into the worker
|
||
-- import Lib
|
||
|
||
-- We're calling the universe U and are doing type in type for now
|
||
|
||
-- Inductive type definitions are similar to Idris, Agda, or Haskell
|
||
data Nat : U where
|
||
Z : Nat
|
||
S : Nat -> Nat
|
||
|
||
-- Multiple names are allowed on the left:
|
||
data Bool : U where
|
||
True False : Bool
|
||
|
||
-- function definitions are equations using dependent pattern matching
|
||
plus : Nat -> Nat -> Nat
|
||
plus Z m = m
|
||
plus (S n) m = S (plus n m)
|
||
|
||
-- we can also have case statements on the right side
|
||
-- the core language includes case statements
|
||
-- here `\` is used for a lambda expression:
|
||
plus' : Nat -> Nat -> Nat
|
||
plus' = \ n m => case n of
|
||
Z => m
|
||
S n => S (plus n m)
|
||
|
||
-- We can define operators, currently only infix
|
||
-- and we allow unicode and letters in operators
|
||
infixl 2 _≡_
|
||
|
||
-- Here is an equality, like Idris, everything goes to the right of the colon
|
||
-- Implicits are denoted with braces `{ }`
|
||
-- unlike idris, you have to declare all of your implicits
|
||
data _≡_ : {A : U} -> A -> A -> U where
|
||
Refl : {A : U} {a : A} -> a ≡ a
|
||
|
||
-- And now the compiler can verify that 1 + 1 = 2
|
||
test : plus (S Z) (S Z) ≡ S (S Z)
|
||
test = Refl
|
||
|
||
-- Ok now we do typeclasses. There isn't any sugar, but we have
|
||
-- search for implicits marked with double brackets.
|
||
|
||
-- Let's say we want a generic `_+_` operator
|
||
infixl 7 _+_
|
||
|
||
-- We don't have records yet, so we define a single constructor
|
||
-- inductive type. Here we also use `∀ A.` which is sugar for `{A : _} ->`
|
||
data Plus : U -> U where
|
||
MkPlus : ∀ A. (A -> A -> A) -> Plus A
|
||
|
||
-- and the generic function that uses it
|
||
-- the double brackets indicate an argument that is solved by search
|
||
_+_ : ∀ A. {{_ : Plus A}} -> A -> A -> A
|
||
_+_ {{MkPlus f}} x y = f x y
|
||
|
||
-- The typeclass is now defined, search will look for functions in scope
|
||
-- that return a type matching (same type constructor) the implicit
|
||
-- and only have implicit arguments (inspired by Agda).
|
||
|
||
-- We make an instance `Plus Nat`
|
||
PlusNat : Plus Nat
|
||
PlusNat = MkPlus plus
|
||
|
||
-- and it now finds the implicits, you'll see the solutions to the
|
||
-- implicits if you hover over the `+`.
|
||
two : Nat
|
||
two = S Z + S Z
|
||
|
||
-- We can leave a hole in an expression with ? and the editor will show us the
|
||
-- scope and expected type (hover to see)
|
||
foo : Nat -> Nat -> Nat
|
||
foo a b = ?
|
||
|
||
-- Newt compiles to javascript, there is a tab to the right that shows the
|
||
-- javascript output. It is not doing erasure (or inlining) yet, so the
|
||
-- code is a little verbose.
|
||
|
||
-- We can define native types:
|
||
|
||
ptype Int : U
|
||
ptype String : U
|
||
ptype Char : U
|
||
|
||
-- The names of these three types are special, primitive numbers, strings,
|
||
-- and characters inhabit them, respectively. We can match on primitives, but
|
||
-- must provide a default case:
|
||
|
||
isVowel : Char -> Bool
|
||
isVowel 'a' = True
|
||
isVowel 'e' = True
|
||
isVowel 'i' = True
|
||
isVowel 'o' = True
|
||
isVowel 'u' = True
|
||
isVowel _ = False
|
||
|
||
-- And primitive functions have a type and a javascript definition:
|
||
|
||
pfunc plusInt : Int -> Int -> Int := "(x,y) => x + y"
|
||
pfunc plusString : String -> String -> String := "(x,y) => x + y"
|
||
|
||
-- We can make them Plus instances:
|
||
|
||
PlusInt : Plus Int
|
||
PlusInt = MkPlus plusInt
|
||
|
||
PlusString : Plus String
|
||
PlusString = MkPlus plusString
|
||
|
||
concat : String -> String -> String
|
||
concat a b = a + b
|
||
|
||
-- Now we define Monad
|
||
|
||
data Monad : (U -> U) -> U where
|
||
MkMonad : {m : U -> U} ->
|
||
({a : U} -> a -> m a) ->
|
||
({a b : U} -> m a -> (a -> m b) -> m b) ->
|
||
Monad m
|
||
|
||
pure : ∀ m. {{Monad m}} -> {a : U} -> a -> m a
|
||
pure {{MkMonad p _}} a = p a
|
||
|
||
-- we can declare multiple infix operators at once
|
||
infixl 1 _>>=_ _>>_
|
||
|
||
_>>=_ : ∀ m a b. {{Monad m}} -> m a -> (a -> m b) -> m b
|
||
_>>=_ {{MkMonad _ b}} ma amb = b ma amb
|
||
|
||
_>>_ : ∀ m a b. {{Monad m}} -> m a -> m b -> m b
|
||
ma >> mb = ma >>= (λ _ => mb)
|
||
|
||
-- That's our Monad typeclass, now let's make a List monad
|
||
|
||
infixr 3 _::_
|
||
data List : U -> U where
|
||
Nil : ∀ A. List A
|
||
_::_ : ∀ A. A -> List A -> List A
|
||
|
||
infixr 7 _++_
|
||
_++_ : ∀ a. List a -> List a -> List a
|
||
Nil ++ ys = ys
|
||
(x :: xs) ++ ys = x :: (xs ++ ys)
|
||
|
||
bindList : ∀ a b. List a -> (a -> List b) -> List b
|
||
bindList Nil f = Nil
|
||
bindList (x :: xs) f = f x ++ bindList xs f
|
||
|
||
-- Both `\` and `λ` work for lambda expressions:
|
||
MonadList : Monad List
|
||
MonadList = MkMonad (λ a => a :: Nil) bindList
|
||
|
||
-- We'll want Pair below too. `,` has been left for use as an operator.
|
||
-- Also we see that → can be used in lieu of ->
|
||
infixr 1 _,_ _×_
|
||
data _×_ : U → U → U where
|
||
_,_ : ∀ A B. A → B → A × B
|
||
|
||
-- The _>>=_ operator is used for desugaring do blocks
|
||
|
||
prod : ∀ A B. List A → List B → List (A × B)
|
||
prod xs ys = do
|
||
x <- xs
|
||
y <- ys
|
||
pure (x, y)
|