Files
newt/playground/samples/Tour.newt
2025-07-18 20:47:45 -04:00

227 lines
5.7 KiB
Agda
Raw Blame History

/-
This is newt, a self-hosted, dependent typed programming language that
compiles to javascript and borrows a lot of syntax from Idris and Agda.
This page is a very simple web playground based on the codemirror editor.
It runs newt in a web worker.
Block comments follow Lean because they're easier to type on a
US keyboard.
-/
-- 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, Prelude is not imported by default, only explicitly
-- imported modules are in scope
-- commented out because we redefine parts of Prelude in this file.
-- import Prelude
-- 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
-- Nat-shaped data is turned into numbers in codegen.
-- Multiple names are allowed on the left:
data Bool : U where
True False : Bool
-- Enum shaped data becomes a bare string (the constructor name) in codegen.
-- 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. Mixfix is supported, but we don't
-- allow ambiguity (so you can't have both [_] and [_,_]). See
-- the Reasoning.newt sample file for a mixfix example.
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 _≡_ : {0 A : U} -> A -> A -> U where
Refl : {0 A : U} {0 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. `class` and `instance` are sugar for
-- ordinary data and functions:
-- Let's say we want a generic `_+_` operator
infixl 7 _+_
class Add a where
_+_ : a -> a -> a
instance Add Nat where
Z + m = m
(S n) + m = S (n + m)
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. There is some erasure, but inlining is not being done
-- yet.
-- We can define native types, if the type is left off, it defaults to U
ptype SomePrimType : U
-- The types Int, String, Char 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:
instance Add Int where
_+_ = plusInt
instance Add String where
_+_ = plusString
concat : String -> String -> String
concat a b = a + b
-- Now we define Monad
class Monad (m : U -> U) where
pure : a. a -> m a
bind : a b. m a -> (a -> m b) -> m b
/-
This desugars to:
data Monad : (m : U -> U) -> U where
MkMonad : {m : U -> U} ->
(pure : {a : _} -> a -> m a) ->
(bind : {a : _} -> {b : _} -> m a -> a -> m b -> m b) ->
Monad m
pure : {m : U -> U} -> {{_ : Monad m}} -> {a : _} -> a -> m a
pure {m} {{MkMonad pure bind}} = pure
bind : {m : U -> U} -> {{_ : Monad m}} -> {a : _} -> {b : _} -> m a -> a -> m b -> m b
bind {m} {{MkMonad pure bind}} = bind
-/
-- we can declare multiple infix operators at once
infixl 1 _>>=_ _>>_
_>>=_ : m a b. {{Monad m}} -> m a -> (a -> m b) -> m b
_>>=_ ma amb = bind ma amb
_>>_ : m a b. {{Monad m}} -> m a -> m b -> m b
ma >> mb = ma >>= (λ _ => mb)
-- Now we define list and show it is a monad. At the moment, I don't
-- have sugar for Lists,
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)
instance Monad List where
pure a = a :: Nil
bind Nil f = Nil
bind (x :: xs) f = f x ++ bind xs f
/-
This desugars to: (the names in guillemots are not user-accessible)
«Monad List,pure» : { a : U } -> a:0 -> List a:1
pure a = _::_ a Nil
«Monad List,bind» : { a : U } -> { b : U } -> (List a) -> (a -> List b) -> List b
bind Nil f = Nil bind (_::_ x xs) f = _++_ (f x) (bind xs f)
«Monad List» : Monad List
«Monad List» = MkMonad «Monad List,pure» «Monad List,bind»
-/
-- We'll want Pair below. `,` 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)
data Unit = MkUnit
ptype World
data IORes a = MkIORes a World
IO : U -> U
IO a = World -> IORes a
instance Monad IO where
bind ma mab = \ w => case ma w of
MkIORes a w => mab a w
pure = MkIORes
pfunc putStrLn uses (MkIORes MkUnit) : String -> IO Unit := `(s) => (w) => {
console.log(s)
return Prelude_MkIORes(null,Prelude_MkUnit,w)
}`
main : IO Unit
main = putStrLn "Hello, World!"