Skip to content

Instantly share code, notes, and snippets.

@VictorTaelin
Created May 6, 2025 21:55
Show Gist options
  • Save VictorTaelin/0272a84276154563d004a45e243dcc47 to your computer and use it in GitHub Desktop.
Save VictorTaelin/0272a84276154563d004a45e243dcc47 to your computer and use it in GitHub Desktop.
Insanely hard prompt - question and answer
-- OldCheck:
-- (omitted)
# The Interaction Calculus
The [Interaction Calculus](https://github.com/VictorTaelin/Interaction-Calculus)
is a minimal term rewriting system inspired by the Lambda Calculus (λC), but
with some key differences:
1. Vars are affine: they can only occur up to one time.
2. Vars are global: they can occur anywhere in the program.
3. There is a new core primitive: the superposition.
An Interaction Calculus term is defined by the following grammar:
```haskell
Term ::=
| VAR: Name
| ERA: "*"
| LAM: "λ" Name "." Term
| APP: "(" Term " " Term ")"
| SUP: "&" Label "{" Term "," Term "}"
| DUP: "!" "&" Label "{" Name "," Name "}" "=" Term ";" Term
```
Where:
- VAR represents a variable.
- ERA represents an erasure.
- LAM represents a lambda.
- APP represents a application.
- SUP represents a superposition.
- DUP represents a duplication.
Lambdas are curried, and work like their λC counterpart, except with a relaxed
scope, and with affine usage. Applications eliminate lambdas, like in λC,
through the beta-reduce (APP-LAM) interaction.
Superpositions work like pairs. Duplications eliminate superpositions through
the DUP-SUP interaction, which works exactly like a pair projection.
What makes SUPs and DUPs unique is how they interact with LAMs and APPs. When a
SUP is applied to an argument, it reduces through the APP-SUP interaction, and
when a LAM is projected, it reduces through the DUP-LAM interaction. This gives
a computational behavior for every possible interaction: there are no runtime
errors on the core Interaction Calculus.
The 'Label' is a numeric value that affects some interactions, like DUP-SUP,
causing terms to commute, instead of annihilate. Read Lafont's Interaction
Combinators paper to learn more.
The core interaction rules are listed below:
```haskell
(* a)
----- APP-ERA
*
(λx.f a)
-------- APP-LAM
x <- a
f
(&L{a,b} c)
----------------- APP-SUP
! &L{c0,c1} = c;
&L{(a c0),(b c1)}
! &L{r,s} = *;
K
-------------- DUP-ERA
r <- *
s <- *
K
! &L{r,s} = λx.f;
K
----------------- DUP-LAM
r <- λx0.f0
s <- λx1.f1
x <- &L{x0,x1}
! &L{f0,f1} = f;
K
! &L{x,y} = &L{a,b};
K
-------------------- DUP-SUP (if equal labels)
x <- a
y <- b
K
! &L{x,y} = &R{a,b};
K
-------------------- DUP-SUP (if different labels)
x <- &R{a0,b0}
y <- &R{a1,b1}
! &L{a0,a1} = a;
! &L{b0,b1} = b;
K
```
Where `x <- t` stands for a global substitution of `x` by `t`.
Since variables are affine, substitutions can be implemented efficiently by just
inserting an entry in a global substitution map (`sub[var] = value`). There is
no need to traverse the target term, or to handle name capture, as long as fresh
variable names are globally unique. Thread-safe substitutions can be performed
with a single atomic-swap.
Below is a pseudocode implementation of these interaction rules:
```python
def app_lam(app, lam):
sub[lam.nam] = app.arg
return lam.bod
def app_sup(app, sup):
x0 = fresh()
x1 = fresh()
a0 = App(sup.lft, Var(x0))
a1 = App(sup.rgt, Var(x1))
return Dup(sup.lab, x0, x1, app.arg, Sup(a0, a1))
def dup_lam(dup, lam):
x0 = fresh()
x1 = fresh()
f0 = fresh()
f1 = fresh()
sub[dup.lft] = Lam(x0, Var(f0))
sub[dup.rgt] = Lam(x1, Var(f1))
sub[lam.nam] = Sup(dup.lab, Var(x0), Var(x1))
return Dup(dup.lab, f0, f1, lam.bod, dup.bod)
def dup_sup(dup, sup):
if dup.lab == sup.lab:
sub[dup.lft] = sup.lft
sub[dup.rgt] = sup.rgt
return dup.bod
```
Terms can be reduced to weak head normal form, which means reducing until the
outermost constructor is a value (LAM, SUP, etc.), or until no more reductions
are possible. Example:
```python
def whnf(term):
while True:
match term:
case Var(nam):
if nam in sub:
term = sub[nam]
else:
return term
case App(fun, arg):
fun = whnf(fun)
match fun.tag:
case LAM: term = app_lam(term, fun)
case SUP: term = app_sup(term, fun)
case _ : return App(fun, arg)
case Dup(lft, rgt, val, bod):
val = whnf(val)
match val.tag:
case LAM: term = dup_lam(term, val)
case SUP: term = dup_sup(term, val)
case _ : return Dup(lft, rgt, val, bod)
case _:
return term
```
Terms can be reduced to full normal form by recursively taking the whnf:
```python
def normal(term):
term = whnf(term)
match term:
case Lam(nam, bod):
bod_nf = normal(bod)
return Lam(nam, bod_nf)
case App(fun, arg):
fun_nf = normal(fun)
arg_nf = normal(arg)
return App(fun_nf, arg_nf)
...
case _:
return term
```
Below are some normalization examples.
Example 0: (simple λ-term)
```haskell
(λx.λt.(t x) λy.y)
------------------ APP-LAM
λt.(t λy.y)
```
Example 1: (larger λ-term)
```haskell
(λb.λt.λf.((b f) t) λT.λF.T)
---------------------------- APP-LAM
λt.λf.((λT.λF.T f) t)
----------------------- APP-LAM
λt.λf.(λF.t f)
-------------- APP-LAM
λt.λf.t
```
Example 2: (global scopes)
```haskell
{x,(λx.λy.y λk.k)}
------------------ APP-LAM
{λk.k,λy.y}
```
Example 3: (superposition)
```haskell
!{a,b} = {λx.x,λy.y}; (a b)
--------------------------- DUP-SUP
(λx.x λy.y)
----------- APP-LAM
λy.y
```
Example 4: (overlap)
```haskell
({λx.x,λy.y} λz.z)
------------------ APP-SUP
! {x0,x1} = λz.z; {(λx.x x0),(λy.y x1)}
--------------------------------------- DUP-LAM
! {f0,f1} = {r,s}; {(λx.x λr.f0),(λy.y λs.f1)}
---------------------------------------------- DUP-SUP
{(λx.x λr.r),(λy.y λs.s)}
------------------------- APP-LAM
{λr.r,(λy.y λs.s)}
------------------ APP-LAM
{λr.r,λs.s}
```
Example 5: (default test term)
The following term can be used to test all interactions:
```haskell
((λf.λx.!{f0,f1}=f;(f0 (f1 x)) λB.λT.λF.((B F) T)) λa.λb.a)
----------------------------------------------------------- 16 interactions
λa.λb.a
```
# Collapsing
An Interaction Calculus term can be collapsed to a superposed tree of pure
Lambda Calculus terms without SUPs and DUPs, by extending the evaluator with the
following collapse interactions:
```haskell
λx.*
------ ERA-LAM
x <- *
*
(f *)
----- ERA-APP
*
λx.&L{f0,f1}
----------------- SUP-LAM
x <- &L{x0,x1}
&L{λx0.f0,λx1.f1}
(f &L{x0,x1})
------------------- SUP-APP
!&L{f0,f1} = f;
&L{(f0 x0),(f1 x1)}
&R{&L{x0,x1},y}
----------------------- SUP-SUP-X (if R>L)
!&R{y0,y1} = y;
&L{&R{x0,x1},&R{y0,y1}}
&R{x,&L{y0,y1}}
----------------------- SUP-SUP-Y (if R>L)
!&R{x0,x1} = x;
&L{&R{x0,x1},&R{y0,y1}}
!&L{x0,x1} = x; K
----------------- DUP-VAR
x0 <- x
x1 <- x
K
!&L{a0,a1} = (f x); K
--------------------- DUP-APP
a0 <- (f0 x0)
a1 <- (f1 x1)
!&L{f0,f1} = f;
!&L{x0,x1} = x;
K
```
module Type where
import Data.Word
import Foreign.Ptr
import Control.Applicative ((<|>))
import Control.Monad (forM)
import Data.Char (chr, ord)
import Data.Char (intToDigit)
import Data.IORef
import Data.List
import Data.Word
import Numeric (showIntAtBase)
import System.IO.Unsafe (unsafePerformIO)
import qualified Data.Map.Strict as MS hiding (map)
-- Core Types
-- ----------
type Tag = Word8
type Lab = Word32
type Loc = Word32
type Term = Word64
type FnID = Word16
type Name = String
type Move = (Name, Core) -- !x = term
data LetT = LAZY | STRI deriving (Eq, Enum)
data MatT = SWI | MAT Word16 | IFL Word16 deriving (Show, Eq)
-- **Note: we're refactoring HVM3's Code Type.**
-- OLD TYPE:
-- data Core
-- = Var Name -- x
-- | Ref Name Word16 [Core] -- @fn
-- | Era -- *
-- | Sup Lab Core Core -- &L{a b}
-- | Dup Lab Name Name Core Core -- ! &L{a b} = v body
-- | Set -- Set
-- | All Core Core -- ∀A.B
-- | Lam Name Core -- λx(F)
-- | App Core Core -- (f x)
-- | Adt Name [Core] -- #Typ{a b ...}
-- | Ctr Name [Core] -- #Ctr{a b ...}
-- | U32 -- U32
-- | W32 Word32 -- 123
-- | Chr Char -- 'a'
-- | Op2 Oper Core Core -- (+ a b)
-- | Let LetT Name Core Core -- ! x = v body
-- | Mat MatT Core [Move] [Case] -- ~ v !moves { cases }
-- deriving (Eq)
-- NEW TYPE:
data Core
= Var Name -- x
| Ref Name FnID [Core] -- @fun(a,b...)
| Let LetT Name Core Core -- !x=v;f
| Era -- *
| Sup Lab Core Core -- &L{a,b}
| Dup Lab Name Name Core Core -- !&L{a,b}=v;f
| Set -- Set
| Emp -- ⊥
| Efq Core [Move] -- ~x!ms{}
| Uni -- ⊤
| Nil -- ()
| Use Core [Move] Core -- ~x!ms{():f}
| U32 -- U32
| W32 Word32 -- 12345
| Swi Core [Move] Core Core -- ~x!ms{0:z;+:s}
| Op2 Oper Core Core -- (+ a b)
| Sig Core Core -- ΣA.B
| Tup Core Core -- (a,b)
| Get Core [Move] Core -- ~x!ms{(,):f}
| All Core Core -- ∀A.B
| Lam Name Core -- λx.F
| App Core Core -- (f x)
| Ann Bool Core Core -- <x:T>
deriving (Eq)
-- The changes include:
-- - Addition of Type-Formers, like Emp, All, Sig, etc.
-- - Addition of empty: Emp/Efq
-- - Addition of unit: Uni/Nil/Use
-- - Addition of pairs: Sig/Tup/Get (behaves like a 0-label Sup/Dup)
-- - Removal of ADT/CTR, in favor of more primitive types
-- - Removal of CHR (which was just another W32)
-- - Removal of the generic MAT, in favor of specific pattern-matches:
-- - - Efq: for the Empty type
-- - - Use: for the Unit type
-- - - Swi: for the U32 type
-- - - Get: for the Sigma type
-- - For simplicity, now, SWI is always ~x{0:z;+:s} (no multi-number switch)
-- - FWD has been removed (it was unused)
-- **Our goal is to update the whole codebase to reflect this change.**
--
-- INSTRUCTIONS:
--
-- When asked to fill below a commented-out code, you must rewrite it completely, updating it to comply to the changes above.
-- Remove discarded cases (like CTR), keep old cases (like Lam/App) verbatin (important: don't change existing definitions!), include new/missing cases (like Tup).
-- Preserve original comments and everything else. Avoid loss of information.
--
-- When asked to implement a missing interaction, reason about it longer, to make sure you get it right.
-- For example, the missing DUP-ALL interaction is:
-- !&L{x0,x1}=∀A.B
-- ----------------- DUP-ALL
-- !&L{A0,A1}=A
-- !&L{B0,B1}=B
-- &L{∀A0.B0,∀A1.B1}
-- Because All doesn't bind a variable.
-- Getting these right demands reasoning.
--
-- NOTES:
--
-- The following terms bind variables:
-- - `λx.f`: a lambda binds 'x' in its body, 'f'
-- - `!x=v;f`; a let binds 'x' in its body, 'f'
-- The following terms DO NOT bind variables:
-- - `~x{}`
-- - `~x{():f}`
-- - `~x{(,):f}`
-- - `~x{0:z;+:s}`
-- - `ΣA.B`
-- - `∀A.B`
-- Instead, we use lambdas *inside* them. For example:
-- @id = λx.~x{0:0;+:λp.(+ 1 @id(p))}
-- @fst = λx.~x{(,):λa.λb.a}
-- Below is the complete list of new elims/intros/types:
-- Eliminators:
-- - APP
-- - USE
-- - GET
-- - SWI
-- - DUP
-- - OPX
-- - OPY
-- Introducers:
-- - ERA
-- - SUP
-- - NIL
-- - W32
-- - TUP
-- - LAM
-- Type-Formers:
-- - SET
-- - EMP
-- - UNI
-- - U32
-- - SIG
-- - ALL
-- STYLE GUIDE:
-- - Always order cases in the SAME order as they were defined on the Core type.
-- - As much as possible, favor SINGLE-LETTER field names, or TWO-LETTER field names (on list-like fields), inspired by the letters used in the comments on the Core type.
-- - For example, instead of `format (Use moves body) = ...`, write `format (Use ms b) = ...`
-- - Since Haskell doesn't accept uppercase variable names, prepend an underscore. Example: `format (All _A _B) = ...`.
-- - Do NOT use the ' (apostrophe) character in variable names. Prefer underscores. Example: `a_` instead of `a'`
-- - In general, attempt to mimic the original coding style as close as possible.
-- - KEEP COMMENTS, ERROR HANDLERS, ETC. FROM THE ORIGINAL CODE - DON'T LOSE INFORMATION
data Oper
= OP_ADD | OP_SUB | OP_MUL | OP_DIV
| OP_MOD | OP_EQ | OP_NE | OP_LT
| OP_GT | OP_LTE | OP_GTE | OP_AND
| OP_OR | OP_XOR | OP_LSH | OP_RSH
deriving (Eq, Enum)
-- A top-level function, including:
-- - copy: true when ref-copy mode is enabled
-- - args: a list of (isArgStrict, argName) pairs
-- - core: the function's body
-- Note: ref-copy improves C speed, but increases interaction count
type Func = ((Bool, [(Bool,String)]), Core)
-- Set of labels in a function's body
type HasLab = (MS.Map Lab ())
-- data Book = Book
-- { fidToFun :: MS.Map Word16 Func -- func id to Func object
-- , fidToLab :: MS.Map Word16 HasLab -- func id to dup labels used
-- , fidToNam :: MS.Map Word16 Name -- func id to name
-- , namToFid :: MS.Map Name Word16 -- func name to id
-- , cidToAri :: MS.Map Word16 Word16 -- ctor id to field count (arity)
-- , cidToLen :: MS.Map Word16 Word16 -- ctor id to cases length (ADT ctors)
-- , cidToCtr :: MS.Map Word16 Name -- ctor id to name
-- , ctrToCid :: MS.Map Name Word16 -- ctor name to id
-- , cidToADT :: MS.Map Word16 Word16 -- ctor id to ADT id (first ADT cid)
-- , freshLab :: Lab -- auto dup label counter
-- } deriving (Show, Eq)
data Book = Book
{ fidToFun :: MS.Map FnID Func -- func id to Func object
, fidToLab :: MS.Map FnID HasLab -- func id to dup labels used
, fidToNam :: MS.Map FnID Name -- func id to name
, namToFid :: MS.Map Name FnID -- func name to id
, freshLab :: Lab -- auto dup label counter
} deriving (Show, Eq)
-- Runtime Types
-- -------------
type HVM = IO
-- Constants
-- ---------
-- -- Tags
-- _DP0_ = 0x00 :: Tag
-- _DP1_ = 0x01 :: Tag
-- _VAR_ = 0x02 :: Tag
-- _FWD_ = 0x03 :: Tag
-- _REF_ = 0x04 :: Tag
-- _LET_ = 0x05 :: Tag
-- _APP_ = 0x06 :: Tag
-- _MAT_ = 0x08 :: Tag
-- _IFL_ = 0x09 :: Tag
-- _SWI_ = 0x0A :: Tag
-- _OPX_ = 0x0B :: Tag
-- _OPY_ = 0x0C :: Tag
-- _ERA_ = 0x0D :: Tag
-- _LAM_ = 0x0E :: Tag
-- _SUP_ = 0x0F :: Tag
-- _CTR_ = 0x10 :: Tag
-- _W32_ = 0x11 :: Tag
-- _CHR_ = 0x12 :: Tag
-- Tags
_DP0_ = 0x00 :: Tag
_DP1_ = 0x01 :: Tag
_VAR_ = 0x02 :: Tag
_REF_ = 0x03 :: Tag
_LET_ = 0x04 :: Tag
_ERA_ = 0x05 :: Tag
_SUP_ = 0x06 :: Tag
_SET_ = 0x08 :: Tag
_EMP_ = 0x09 :: Tag
_EFQ_ = 0x0A :: Tag
_UNI_ = 0x0B :: Tag
_NIL_ = 0x0C :: Tag
_USE_ = 0x0D :: Tag
_U32_ = 0x0E :: Tag
_W32_ = 0x0F :: Tag
_SWI_ = 0x10 :: Tag
_OPX_ = 0x11 :: Tag
_OPY_ = 0x12 :: Tag
_SIG_ = 0x13 :: Tag
_TUP_ = 0x14 :: Tag
_GET_ = 0x15 :: Tag
_ALL_ = 0x16 :: Tag
_LAM_ = 0x17 :: Tag
_APP_ = 0x18 :: Tag
_ANN_ = 0x19 :: Tag
_VAL_ = 0x1A :: Tag
-- Let Types
modeT :: Lab -> LetT
modeT 0x00 = LAZY
modeT 0x01 = STRI
modeT mode = error $ "unknown mode: " ++ show mode
-- Primitive Functions
_DUP_F_ = 0xFFFF :: Lab
_SUP_F_ = 0xFFFE :: Lab
_LOG_F_ = 0xFFFD :: Lab
primitives :: [(String, Word16)]
primitives =
[ ("SUP", fromIntegral _SUP_F_)
, ("DUP", fromIntegral _DUP_F_)
, ("LOG", fromIntegral _LOG_F_)
]
-- Utils
-- -----
-- Getter function for maps
mget :: (Ord k, Show k) => MS.Map k a -> k -> a
mget map key =
case MS.lookup key map of
Just val -> val
Nothing -> error $ "key not found: " ++ show key
funArity :: Book -> Word16 -> Word16
funArity book fid
| fid == fromIntegral _SUP_F_ = 3
| fid == fromIntegral _DUP_F_ = 3
| fid == fromIntegral _LOG_F_ = 1
| otherwise = case MS.lookup fid (fidToFun book) of
Just ((_, args), _) ->
fromIntegral (length args)
Nothing ->
error $ "Function ID not found: " ++ show fid
-- Stringification
-- ---------------
padLeft :: String -> Int -> Char -> String
padLeft str n c = replicate (n - length str) c ++ str
showHex :: Word64 -> String
showHex x = showIntAtBase 16 intToDigit (fromIntegral x) ""
showName :: Int -> String
showName n = go (n + 1) "" where
go n ac | n == 0 = ac
| otherwise = go q (chr (ord 'a' + r) : ac)
where (q,r) = quotRem (n - 1) 26
-- showTag :: Tag -> String
-- showTag tag
-- | tag == _DP0_ = "DP0"
-- | tag == _DP1_ = "DP1"
-- | tag == _VAR_ = "VAR"
-- | tag == _FWD_ = "FWD"
-- | tag == _REF_ = "REF"
-- | tag == _LET_ = "LET"
-- | tag == _APP_ = "APP"
-- | tag == _MAT_ = "MAT"
-- | tag == _IFL_ = "IFL"
-- | tag == _SWI_ = "SWI"
-- | tag == _OPX_ = "OPX"
-- | tag == _OPY_ = "OPY"
-- | tag == _ERA_ = "ERA"
-- | tag == _LAM_ = "LAM"
-- | tag == _SUP_ = "SUP"
-- | tag == _CTR_ = "CTR"
-- | tag == _W32_ = "W32"
-- | tag == _CHR_ = "CHR"
-- | otherwise = error $ "unknown tag: " ++ show tag
showTag :: Tag -> String
showTag tag
| tag == _DP0_ = "DP0"
| tag == _DP1_ = "DP1"
| tag == _VAR_ = "VAR"
| tag == _REF_ = "REF"
| tag == _LET_ = "LET"
| tag == _ERA_ = "ERA"
| tag == _SUP_ = "SUP"
| tag == _SET_ = "SET"
| tag == _EMP_ = "EMP"
| tag == _EFQ_ = "EFQ"
| tag == _UNI_ = "UNI"
| tag == _NIL_ = "NIL"
| tag == _USE_ = "USE"
| tag == _U32_ = "U32"
| tag == _W32_ = "W32"
| tag == _SWI_ = "SWI"
| tag == _OPX_ = "OPX"
| tag == _OPY_ = "OPY"
| tag == _SIG_ = "SIG"
| tag == _TUP_ = "TUP"
| tag == _GET_ = "GET"
| tag == _ALL_ = "ALL"
| tag == _LAM_ = "LAM"
| tag == _APP_ = "APP"
| tag == _ANN_ = "ANN"
| tag == _VAL_ = "VAL"
| otherwise = error $ "unknown tag: " ++ show tag
showLab :: Lab -> String
showLab lab = padLeft (showHex (fromIntegral lab)) 6 '0'
showLoc :: Loc -> String
showLoc loc = padLeft (showHex (fromIntegral loc)) 8 '0'
instance Show Oper where
show OP_ADD = "+"
show OP_SUB = "-"
show OP_MUL = "*"
show OP_DIV = "/"
show OP_MOD = "%"
show OP_EQ = "=="
show OP_NE = "!="
show OP_LT = "<"
show OP_GT = ">"
show OP_LTE = "<="
show OP_GTE = ">="
show OP_AND = "&"
show OP_OR = "|"
show OP_XOR = "^"
show OP_LSH = "<<"
show OP_RSH = ">>"
instance Show LetT where
show LAZY = ""
show STRI = "."
showCore :: Core -> String
showCore = format
where
format :: Core -> String
format (Var k) = k
format (Ref k i xs) =
let xs' = intercalate " " (map showCore xs) in
concat ["@", k, "(", xs', ")"]
format (Let m k v f) =
let v' = showCore v in
let f' = showCore f in
if k == "" then
concat [v', "\n", f']
else
concat ["! ", show m, k, " = ", v', "\n", f']
format Era = "*"
format (Sup l a b) =
let a' = showCore a in
let b' = showCore b in
concat ["&", show l, "{", a', ",", b', "}"]
format (Dup l x y v f) =
let v' = showCore v in
let f' = showCore f in
concat ["! &", show l, "{", x, ",", y, "} = ", v', "\n", f']
format Set = "Set"
format Emp = "⊥"
format (Efq c ms) =
let c' = showCore c in
let ms' = concatMap (\(k,v) -> concat [" !", k, "=", showCore v]) ms in
concat ["~", c', ms', "{}"]
format Uni = "⊤"
format Nil = "()"
format (Use c ms b) =
let c' = showCore c in
let ms' = concatMap (\(k,v) -> concat [" !", k, "=", showCore v]) ms in
let b' = showCore b in
concat ["~", c', ms', "{():", b', "}"]
format U32 = "U32"
format (W32 v) = show v
format (Swi c ms z s) =
let c' = showCore c in
let ms' = concatMap (\(k,v) -> concat [" !", k, "=", showCore v]) ms in
let z' = showCore z in
let s' = showCore s in
concat ["~", c', ms', "{0:", z', ";+:", s', "}"]
format (Op2 o a b) =
let a' = showCore a in
let b' = showCore b in
concat ["(", show o, " ", a', " ", b', ")"]
format (Sig _A _B) =
let a' = showCore _A in
let b' = showCore _B in
concat ["Σ", a', ".", b']
format (Tup a b) =
let a' = showCore a in
let b' = showCore b in
concat ["(", a', ",", b', ")"]
format (Get c ms b) =
let c' = showCore c in
let ms' = concatMap (\(k,v) -> concat [" !", k, "=", showCore v]) ms in
let b' = showCore b in
concat ["~", c', ms', "{(,):", b', "}"]
format (All _A _B) =
let a' = showCore _A in
let b' = showCore _B in
concat ["∀", a', ".", b']
format (Lam x f) =
let f' = showCore f in
concat ["λ", x, ".", f']
format (App f x) =
let f' = showCore f in
let x' = showCore x in
concat ["(", f', " ", x', ")"]
format (Ann c x t) =
let x' = showCore x in
let t' = showCore t in
concat ["<", x', " : ", t', ">"]
renamer :: IORef (MS.Map String String) -> Core -> IO Core
renamer names core = case core of
Var k -> do
k' <- genName names k
return $ Var k'
Ref k i xs -> do
xs' <- mapM (renamer names) xs
return $ Ref k i xs'
Let m k v f -> do
k' <- genName names k
v' <- renamer names v
f' <- renamer names f
return $ Let m k' v' f'
Era ->
return Era
Sup l a b -> do
a' <- renamer names a
b' <- renamer names b
return $ Sup l a' b'
Dup l x y v f -> do
x' <- genName names x
y' <- genName names y
v' <- renamer names v
f' <- renamer names f
return $ Dup l x' y' v' f'
Set ->
return Set
Emp ->
return Emp
Efq c ms -> do
c' <- renamer names c
ms' <- forM ms $ \(k,v) -> do v' <- renamer names v; return (k,v')
return $ Efq c' ms'
Uni ->
return Uni
Nil ->
return Nil
Use c ms b -> do
c' <- renamer names c
ms' <- forM ms $ \(k,v) -> do v' <- renamer names v; return (k,v')
b' <- renamer names b
return $ Use c' ms' b'
U32 ->
return U32
W32 v ->
return $ W32 v
Swi c ms z s -> do
c' <- renamer names c
ms' <- forM ms $ \(k,v) -> do v' <- renamer names v; return (k,v')
z' <- renamer names z
s' <- renamer names s
return $ Swi c' ms' z' s'
Op2 o a b -> do
a' <- renamer names a
b' <- renamer names b
return $ Op2 o a' b'
Sig _A _B -> do
_A' <- renamer names _A
_B' <- renamer names _B
return $ Sig _A' _B'
Tup a b -> do
a' <- renamer names a
b' <- renamer names b
return $ Tup a' b'
Get c ms b -> do
c' <- renamer names c
ms' <- forM ms $ \(k,v) -> do v' <- renamer names v; return (k,v')
b' <- renamer names b
return $ Get c' ms' b'
All _A _B -> do
_A' <- renamer names _A
_B' <- renamer names _B
return $ All _A' _B'
Lam x f -> do
x' <- genName names x
f' <- renamer names f
return $ Lam x' f'
App f x -> do
f' <- renamer names f
x' <- renamer names x
return $ App f' x'
Ann c x t -> do
x' <- renamer names x
t' <- renamer names t
return $ Ann c x' t'
rename :: Core -> Core
rename core = unsafePerformIO $ do
names <- newIORef MS.empty
renamer names core
genName :: IORef (MS.Map String String) -> String -> IO String
genName names name =
atomicModifyIORef' names $ \map ->
case MS.lookup name map of
Just val -> (map, val)
Nothing ->
let new = showName (MS.size map)
map' = MS.insert name new map
in (map', new)
instance Show Core where
show = showCore . rename
-- COMPLETE INTERACTION TABLE (updated)
-- @foo(a,b...)
-- ----------------- REF
-- book[foo](a,b...)
-- Let:
-- ! x = v ; f
-- ----------- LET
-- x <- v
-- f
-- (* a)
-- ----- APP-ERA
-- *
-- (λx.f a)
-- -------- APP-LAM
-- x <- a
-- f
-- (&L{f0,f1} a)
-- ------------------- APP-SUP
-- ! &L{a0,a1} = a;
-- &L{(f0 x0),(f1 x1)}
-- (() a)
-- ------ APP-NIL
-- error
-- ((f0,f1) a)
-- ----------- APP-TUP
-- error
-- (123 a)
-- ------- APP-W32
-- error
-- (T a)
-- ----- APP-TYP
-- error
-- ~*{():f}
-- -------- USE-ERA
-- *
-- ~&L{a,b}{():f}
-- ----------------------- USE-SUP
-- ! &L{f0,f1} = f;
-- &L{~a{():f0},~b{():f1}}
-- ~λx.g{():f}
-- ----------- USE-LAM
-- error
-- ~(){():f}
-- --------- USE-NIL
-- f
-- ~(p,q){():f}
-- ------------ USE-TUP
-- error
-- ~123{():f}
-- ---------- USE-W32
-- error
-- ~T{():f}
-- -------- USE-TYP
-- error
-- ~*{(,):f}
-- --------- GET-ERA
-- *
-- ~&L{a,b}{(,): f}
-- ------------------------- GET-SUP
-- ! &L{f0,f1} = f;
-- &L{~a{(,):f0},~b{(,):f1}}
-- ~(){(,):f}
-- ---------- GET-NIL
-- error
-- ~(a,b){(,):f}
-- ------------- GET-TUP
-- (f a b)
-- ~λx.g{(,):f}
-- ------------ GET-LAM
-- error
-- ~123{(,):f}
-- ----------- GET-W32
-- error
-- ~TYP{(,):f}
-- ----------- GET-TYP
-- error
-- ~*{0:z;+:s}
-- ----------- SWI-ERA
-- *
-- ~&L{a,b}{0:z;+:s}
-- ------------------------------- SWI-SUP
-- ! &L{z0,z1} = z;
-- ! &L{s0,s1} = s;
-- &L{~a{0:z0;+:s0},~b{0:z1;+:s1}}
-- ~(){0:z;+:s}
-- ------------ SWI-NIL
-- error
-- ~(a,b){0:z;+:s}
-- --------------- SWI-TUP
-- error
-- ~λx.g{0:z;+:s}
-- -------------- SWI-LAM
-- error
-- ~n{0:z;+:s}
-- ----------- SWI-W32
-- if n = 0:
-- z
-- else:
-- (s (n-1))
-- ~T{0:z;+:s}
-- ----------- SWI-TYP
-- error
-- ! &L{r,s} = *
-- ------------- DUP-ERA
-- r <- *
-- s <- *
-- ! &L{x,y} = &L{a,b}
-- ------------------- DUP-SUP
-- x <- a
-- y <- b
-- ! &L{r,s} = λx.f
-- ---------------- DUP-LAM
-- r <- λx0.f0
-- s <- λx1.f1
-- x <- &L{x0,x1}
-- ! &L{f0,f1} = f
-- ! &L{x,y} = ()
-- -------------- DUP-NIL
-- x <- ()
-- y <- ()
-- ! &L{x,y} = (a,b)
-- ----------------- DUP-TUP
-- ! &L{a0,a1} = a
-- ! &L{b0,b1} = b
-- x <- &L{a0,b0}
-- y <- &L{a1,b1}
-- ! &L{x,y} = 123
-- --------------- DUP-W32
-- x <- 123
-- y <- 123
-- ! &L{x,y} = Set
-- --------------- DUP-SET
-- x <- Set
-- y <- Set
-- ! &L{x,y} = ⊥
-- ------------- DUP-EMP
-- x <- ⊥
-- y <- ⊥
-- ! &L{x,y} = ⊤
-- ------------- DUP-UNI
-- x <- ⊤
-- y <- ⊤
-- ! &L{x,y} = U32
-- -------------- DUP-U32
-- x <- U32
-- y <- U32
-- ! &L{x,y} = ΣA.B
-- ----------------- DUP-SIG
-- ! &L{A0,A1} = A
-- ! &L{B0,B1} = B
-- x <- ΣA0.B0
-- y <- ΣA1.B1
-- ! &L{x,y} = ∀A.B
-- ----------------- DUP-ALL
-- ! &L{A0,A1} = A
-- ! &L{B0,B1} = B
-- x <- ∀A0.B0
-- y <- ∀A1.B1
-- (<op * y)
-- --------- OPX-ERA
-- *
-- (<op λx.f y)
-- ----------- OPX-LAM
-- error
-- (<op &L{a,b} y)
-- ------------------------- OPX-SUP
-- ! &L{y0,y1} = y;
-- &L{(<op a y0),(<op b y1)}
-- (<op () y)
-- ---------- OPX-NIL
-- error
-- (<op (x0,x1) y)
-- --------------- OPX-TUP
-- error
-- (<op x y)
-- --------- OPX-W32
-- (>op y x)
-- (<op T y)
-- --------- OPX-TYP
-- error
-- (>op x *)
-- --------- OPY-ERA
-- *
-- (>op x λy.f)
-- ------------ OPY-LAM
-- error
-- (>op x &L{y0,y1})
-- ----------------------- OPY-SUP
-- &L{>op(x y0),>op(x y1)}
-- (>op x ())
-- ---------- OPY-NIL
-- error
-- (>op x (y0,y1))
-- --------------- OPY-TUP
-- error
-- (>op x y)
-- --------- OPY-W32
-- x <op> y
-- (>op x T)
-- --------- OPY-TYP
-- error
-- ! &L{x,y} = @foo(a,b...)
-- ------------------------ DUP-REF-COPY (when &L not in @foo)
-- ! &L{a0,a1} = a
-- ! &L{b0,b1} = b
-- ...
-- x <- @foo(a0,b0...)
-- y <- @foo(a1,b1...)
-- @foo(&L{ax,ay},b,c...)
-- ---------------------- REF-SUP-COPY (when @L not in @foo)
-- ! &L{bx,by} = b
-- ! &L{cx,by} = c
-- ...
-- &L{@foo(ax,bx,cx...),@foo(ay,by,cy,...)}
{-./Type.hs-}
module Inject where
import Control.Monad (foldM, when, forM_)
import Control.Monad.State
import Data.Bits (shiftL, (.|.))
import Data.Char (ord)
import Data.List (foldr)
import Data.Word
import Debug.Trace
import Foreign
import Type
import qualified Data.Map.Strict as MS
type InjectM a = StateT InjectState HVM a
data InjectState = InjectState
{ args :: MS.Map String Term -- maps var names to binder locations
, vars :: [(String, Loc)] -- list of (var name, usage location) pairs
}
emptyState :: InjectState
emptyState = InjectState MS.empty []
-- injectCore :: Book -> Core -> Loc -> InjectM ()
-- injectCore _ Era loc = do
-- lift $ set loc (termNew _ERA_ 0 0)
-- injectCore _ (Var nam) loc = do
-- argsMap <- gets args
-- case MS.lookup nam argsMap of
-- Just term -> do
-- lift $ set loc term
-- when (head nam /= '&') $ do
-- modify $ \s -> s { args = MS.delete nam (args s) }
-- Nothing -> do
-- modify $ \s -> s { vars = (nam, loc) : vars s }
-- injectCore book (Let mod nam val bod) loc = do
-- let_node <- lift $ allocNode 2
-- modify $ \s -> s { args = MS.insert nam (termNew _VAR_ 0 (let_node + 0)) (args s) }
-- injectCore book val (let_node + 0)
-- injectCore book bod (let_node + 1)
-- lift $ set loc (termNew _LET_ (fromIntegral $ fromEnum mod) let_node)
-- injectCore book (Lam vr0 bod) loc = do
-- lam <- lift $ allocNode 1
-- modify $ \s -> s { args = MS.insert vr0 (termNew _VAR_ 0 (lam + 0)) (args s) }
-- injectCore book bod (lam + 0)
-- lift $ set loc (termNew _LAM_ 0 lam)
-- injectCore book (App fun arg) loc = do
-- app <- lift $ allocNode 2
-- injectCore book fun (app + 0)
-- injectCore book arg (app + 1)
-- lift $ set loc (termNew _APP_ 0 app)
-- injectCore book (Sup lab tm0 tm1) loc = do
-- sup <- lift $ allocNode 2
-- injectCore book tm0 (sup + 0)
-- injectCore book tm1 (sup + 1)
-- lift $ set loc (termNew _SUP_ lab sup)
-- injectCore book (Dup lab dp0 dp1 val bod) loc = do
-- dup <- lift $ allocNode 1
-- modify $ \s -> s
-- { args = MS.insert dp0 (termNew _DP0_ lab dup)
-- $ MS.insert dp1 (termNew _DP1_ lab dup) (args s)
-- }
-- injectCore book val (dup + 0)
-- injectCore book bod loc
-- injectCore book (Ref nam fid arg) loc = do
-- let ari = funArity book fid
-- let lab = fromIntegral fid
-- when (ari /= fromIntegral (length arg)) $ do
-- error $ "Arity mismatch on term: " ++ show (Ref nam fid arg) ++ ". Expected " ++ show ari ++ ", got " ++ show (length arg) ++ "."
-- ref <- lift $ allocNode (fromIntegral ari)
-- sequence_ [injectCore book x (ref + i) | (i,x) <- zip [0..] arg]
-- lift $ set loc (termNew _REF_ lab ref)
-- injectCore book (Ctr nam fds) loc = do
-- let ari = length fds
-- let cid = mget (ctrToCid book) nam
-- let lab = fromIntegral cid
-- ctr <- lift $ allocNode (fromIntegral ari)
-- sequence_ [injectCore book fd (ctr + ix) | (ix,fd) <- zip [0..] fds]
-- lift $ set loc (termNew _CTR_ lab ctr)
-- injectCore book tm@(Mat kin val mov css) loc = do
-- mat <- lift $ allocNode (1 + fromIntegral (length css))
-- injectCore book val (mat + 0)
-- forM_ (zip [0..] css) $ \ (idx, (ctr, fds, bod)) -> do
-- injectCore book (foldr (\x b -> Lam x b) (foldr (\x b -> Lam x b) bod (map fst mov)) fds) (mat + 1 + fromIntegral idx)
-- let tag = case kin of { SWI -> _SWI_ ; (MAT _) -> _MAT_ ; (IFL _) -> _IFL_ }
-- let lab = case kin of { SWI -> fromIntegral $ length css ; (MAT cid) -> fromIntegral cid ; (IFL cid) -> fromIntegral cid }
-- trm <- return $ termNew tag lab mat
-- ret <- foldM (\mat (_, val) -> do
-- app <- lift $ allocNode 2
-- lift $ set (app + 0) mat
-- injectCore book val (app + 1)
-- return $ termNew _APP_ 0 app)
-- trm
-- mov
-- lift $ set loc ret
-- injectCore book (W32 val) loc = do
-- lift $ set loc (termNew _W32_ 0 (fromIntegral val))
-- injectCore book (Chr val) loc = do
-- lift $ set loc (termNew _CHR_ 0 (fromIntegral $ ord val))
-- injectCore book (Op2 opr nm0 nm1) loc = do
-- opx <- lift $ allocNode 2
-- injectCore book nm0 (opx + 0)
-- injectCore book nm1 (opx + 1)
-- lift $ set loc (termNew _OPX_ (fromIntegral $ fromEnum opr) opx)
-- injectCore _ Set loc = do
-- lift $ set loc (termNew _SET_ 0 0)
-- injectCore book (All typ bod) loc = do
-- all_node <- lift $ allocNode 2
-- injectCore book typ (all_node + 0)
-- injectCore book bod (all_node + 1)
-- lift $ set loc (termNew _ALL_ 0 all_node)
-- injectCore book (Adt nam fds) loc = do
-- let ari = length fds
-- let cid = mget (ctrToCid book) nam
-- let lab = fromIntegral cid
-- adt <- lift $ allocNode (fromIntegral ari)
-- sequence_ [injectCore book fd (adt + ix) | (ix,fd) <- zip [0..] fds]
-- lift $ set loc (termNew _ADT_ lab adt)
-- injectCore _ U32 loc = do
-- lift $ set loc (termNew _W32_ 0 0)
-- doInjectCoreAt :: Book -> Core -> Loc -> [(String,Term)] -> HVM Term
-- doInjectCoreAt book core host argList = do
-- (_, state) <- runStateT (injectCore book core host) (emptyState { args = MS.fromList argList })
-- foldM (\m (name, loc) -> do
-- case MS.lookup name (args state) of
-- Just term -> do
-- set loc term
-- return $ MS.delete name m
-- Nothing -> do
-- error $ "Unbound variable: \n\x1b[2m" ++ name ++ "\n\x1b[0mIn term:\n\x1b[2m" ++ Data.List.take 1024 (show core) ++ "...\x1b[0m")
-- (args state)
-- (vars state)
-- got host
-- UPDATED:
injectCore :: Book -> Core -> Loc -> InjectM ()
injectCore _ Era loc = do
lift $ set loc (termNew _ERA_ 0 0)
injectCore _ (Var x) loc = do
argsMap <- gets args
case MS.lookup x argsMap of
Just term -> do
lift $ set loc term
when (head x /= '&') $ do
modify $ \s -> s { args = MS.delete x (args s) }
Nothing -> do
modify $ \s -> s { vars = (x, loc) : vars s }
injectCore book (Ref x i xs) loc = do
let ari = funArity book i
let lab = fromIntegral i
when (ari /= fromIntegral (length xs)) $ do
error $ "Arity mismatch on term: " ++ show (Ref x i xs) ++ ". Expected " ++ show ari ++ ", got " ++ show (length xs) ++ "."
ref <- lift $ allocNode (fromIntegral ari)
sequence_ [injectCore book x (ref + i) | (i,x) <- zip [0..] xs]
lift $ set loc (termNew _REF_ lab ref)
injectCore book (Let m x v f) loc = do
let_node <- lift $ allocNode 2
modify $ \s -> s { args = MS.insert x (termNew _VAR_ 0 (let_node + 0)) (args s) }
injectCore book v (let_node + 0)
injectCore book f (let_node + 1)
lift $ set loc (termNew _LET_ (fromIntegral $ fromEnum m) let_node)
injectCore _ Era loc = do
lift $ set loc (termNew _ERA_ 0 0)
injectCore book (Sup l a b) loc = do
sup <- lift $ allocNode 2
injectCore book a (sup + 0)
injectCore book b (sup + 1)
lift $ set loc (termNew _SUP_ l sup)
injectCore book (Dup l x y v f) loc = do
dup <- lift $ allocNode 1
modify $ \s -> s
{ args = MS.insert x (termNew _DP0_ l dup)
$ MS.insert y (termNew _DP1_ l dup) (args s)
}
injectCore book v (dup + 0)
injectCore book f loc
injectCore _ Set loc = do
lift $ set loc (termNew _SET_ 0 0)
injectCore _ Emp loc = do
lift $ set loc (termNew _EMP_ 0 0)
injectCore book (Efq c ms) loc = do
efq <- lift $ allocNode 1
injectCore book c (efq + 0)
applyMoves book (termNew _EFQ_ 0 efq) ms loc
injectCore _ Uni loc = do
lift $ set loc (termNew _UNI_ 0 0)
injectCore _ Nil loc = do
lift $ set loc (termNew _NIL_ 0 0)
injectCore book (Use c ms b) loc = do
use <- lift $ allocNode 2
injectCore book c (use + 0)
injectCore book b (use + 1)
applyMoves book (termNew _USE_ 0 use) ms loc
injectCore _ U32 loc = do
lift $ set loc (termNew _U32_ 0 0)
injectCore _ (W32 n) loc = do
lift $ set loc (termNew _W32_ 0 (fromIntegral n))
injectCore book (Swi c ms z s) loc = do
swi <- lift $ allocNode 3
injectCore book c (swi + 0)
injectCore book z (swi + 1)
injectCore book s (swi + 2)
applyMoves book (termNew _SWI_ 0 swi) ms loc
injectCore book (Op2 o a b) loc = do
opx <- lift $ allocNode 2
injectCore book a (opx + 0)
injectCore book b (opx + 1)
lift $ set loc (termNew _OPX_ (fromIntegral $ fromEnum o) opx)
injectCore book (Sig _A _B) loc = do
sig <- lift $ allocNode 2
injectCore book _A (sig + 0)
injectCore book _B (sig + 1)
lift $ set loc (termNew _SIG_ 0 sig)
injectCore book (Tup a b) loc = do
tup <- lift $ allocNode 2
injectCore book a (tup + 0)
injectCore book b (tup + 1)
lift $ set loc (termNew _TUP_ 0 tup)
injectCore book (Get c ms b) loc = do
get <- lift $ allocNode 2
injectCore book c (get + 0)
injectCore book b (get + 1)
applyMoves book (termNew _GET_ 0 get) ms loc
injectCore book (All _A _B) loc = do
all_node <- lift $ allocNode 2
injectCore book _A (all_node + 0)
injectCore book _B (all_node + 1)
lift $ set loc (termNew _ALL_ 0 all_node)
injectCore book (Lam x f) loc = do
lam <- lift $ allocNode 1
modify $ \s -> s { args = MS.insert x (termNew _VAR_ 0 (lam + 0)) (args s) }
injectCore book f (lam + 0)
lift $ set loc (termNew _LAM_ 0 lam)
injectCore book (App f x) loc = do
app <- lift $ allocNode 2
injectCore book f (app + 0)
injectCore book x (app + 1)
lift $ set loc (termNew _APP_ 0 app)
injectCore book (Ann c x t) loc = do
ann <- lift $ allocNode 2
injectCore book x (ann + 0)
injectCore book t (ann + 1)
lift $ set loc (termNew _ANN_ (if c then 1 else 0) ann)
applyMoves :: Book -> Term -> [Move] -> Loc -> InjectM ()
applyMoves book base ms loc = do
result <- foldM (\term (_, v) -> do
app <- lift $ allocNode 2
lift $ set (app + 0) term
injectCore book v (app + 1)
return $ termNew _APP_ 0 app)
base
ms
lift $ set loc result
doInjectCoreAt :: Book -> Core -> Loc -> [(String,Term)] -> HVM Term
doInjectCoreAt book core host argList = do
(_, state) <- runStateT (injectCore book core host) (emptyState { args = MS.fromList argList })
_ <- foldM (\m (name, loc) -> do
case MS.lookup name m of
Just term -> do
set loc term
return $ MS.delete name m
Nothing -> do
error $ "Unbound variable: \n\x1b[2m" ++ name ++ "\n\x1b[0mIn term:\n\x1b[2m" ++ Prelude.take 1024 (show core) ++ "...\x1b[0m")
(args state)
(vars state)
got host
{-./Type.hs-}
{-./Foreign.hs-}
-- module Reduce where
-- import Control.Monad (when, forM, forM_)
-- import Data.Word
-- import Collapse
-- import Extract
-- import Foreign
-- import Inject
-- import Type
-- import System.Exit
-- import qualified Data.Map.Strict as MS
-- reduceAt :: Bool -> (Book -> Loc -> HVM Term)
-- reduceAt debug book host = do
-- term <- got host
-- let tag = termTag term
-- let lab = termLab term
-- let loc = termLoc term
-- when debug $ do
-- root <- doExtractCoreAt gotT book 0
-- core <- doExtractCoreAt gotT book host
-- putStrLn $ "reduce: " ++ showTerm term
-- -- putStrLn $ "---------------- CORE: "
-- -- putStrLn $ showCore core
-- putStrLn $ "---------------- ROOT: "
-- putStrLn $ showCore (doLiftDups root)
-- case tag of
-- t | t == _LET_ -> do
-- case modeT lab of
-- LAZY -> do
-- val <- got (loc + 0)
-- cont host (reduceLet term val)
-- STRI -> do
-- val <- reduceAt debug book (loc + 0)
-- cont host (reduceLet term val)
-- t | t == _APP_ -> do
-- fun <- reduceAt debug book (loc + 0)
-- case termTag fun of
-- t | t == _ERA_ -> cont host (reduceAppEra term fun)
-- t | t == _LAM_ -> cont host (reduceAppLam term fun)
-- t | t == _SUP_ -> cont host (reduceAppSup term fun)
-- t | t == _CTR_ -> cont host (reduceAppCtr term fun)
-- t | t == _W32_ -> cont host (reduceAppW32 term fun)
-- t | t == _CHR_ -> cont host (reduceAppW32 term fun)
-- _ -> set (loc + 0) fun >> return term
-- t | t == _MAT_ -> do
-- val <- reduceAt debug book (loc + 0)
-- case termTag val of
-- t | t == _ERA_ -> cont host (reduceMatEra term val)
-- t | t == _LAM_ -> cont host (reduceMatLam term val)
-- t | t == _SUP_ -> cont host (reduceMatSup term val)
-- t | t == _CTR_ -> cont host (reduceMatCtr term val)
-- t | t == _W32_ -> cont host (reduceMatW32 term val)
-- t | t == _CHR_ -> cont host (reduceMatW32 term val)
-- _ -> set (loc + 0) val >> return term
-- t | t == _IFL_ -> do
-- val <- reduceAt debug book (loc + 0)
-- case termTag val of
-- t | t == _ERA_ -> cont host (reduceMatEra term val)
-- t | t == _LAM_ -> cont host (reduceMatLam term val)
-- t | t == _SUP_ -> cont host (reduceMatSup term val)
-- t | t == _CTR_ -> cont host (reduceMatCtr term val)
-- t | t == _W32_ -> cont host (reduceMatW32 term val)
-- t | t == _CHR_ -> cont host (reduceMatW32 term val)
-- _ -> set (loc + 0) val >> return term
-- t | t == _SWI_ -> do
-- val <- reduceAt debug book (loc + 0)
-- case termTag val of
-- t | t == _ERA_ -> cont host (reduceMatEra term val)
-- t | t == _LAM_ -> cont host (reduceMatLam term val)
-- t | t == _SUP_ -> cont host (reduceMatSup term val)
-- t | t == _CTR_ -> cont host (reduceMatCtr term val)
-- t | t == _W32_ -> cont host (reduceMatW32 term val)
-- t | t == _CHR_ -> cont host (reduceMatW32 term val)
-- _ -> set (loc + 0) val >> return term
-- t | t == _OPX_ -> do
-- val <- reduceAt debug book (loc + 0)
-- case termTag val of
-- t | t == _ERA_ -> cont host (reduceOpxEra term val)
-- t | t == _LAM_ -> cont host (reduceOpxLam term val)
-- t | t == _SUP_ -> cont host (reduceOpxSup term val)
-- t | t == _CTR_ -> cont host (reduceOpxCtr term val)
-- t | t == _W32_ -> cont host (reduceOpxW32 term val)
-- t | t == _CHR_ -> cont host (reduceOpxW32 term val)
-- _ -> set (loc + 0) val >> return term
-- t | t == _OPY_ -> do
-- val <- reduceAt debug book (loc + 0)
-- case termTag val of
-- t | t == _ERA_ -> cont host (reduceOpyEra term val)
-- t | t == _LAM_ -> cont host (reduceOpyLam term val)
-- t | t == _SUP_ -> cont host (reduceOpySup term val)
-- t | t == _CTR_ -> cont host (reduceOpyCtr term val)
-- t | t == _W32_ -> cont host (reduceOpyW32 term val)
-- t | t == _CHR_ -> cont host (reduceOpyW32 term val)
-- _ -> set (loc + 0) val >> return term
-- t | t == _DP0_ -> do
-- sb0 <- got (loc + 0)
-- if termGetBit sb0 == 0
-- then do
-- val <- reduceAt debug book (loc + 0)
-- case termTag val of
-- t | t == _ERA_ -> cont host (reduceDupEra term val)
-- t | t == _LAM_ -> cont host (reduceDupLam term val)
-- t | t == _SUP_ -> cont host (reduceDupSup term val)
-- t | t == _CTR_ -> cont host (reduceDupCtr term val)
-- t | t == _W32_ -> cont host (reduceDupW32 term val)
-- t | t == _CHR_ -> cont host (reduceDupW32 term val)
-- _ -> set (loc + 0) val >> return term
-- else do
-- set host (termRemBit sb0)
-- reduceAt debug book host
-- t | t == _DP1_ -> do
-- sb1 <- got (loc + 0)
-- if termGetBit sb1 == 0
-- then do
-- val <- reduceAt debug book (loc + 0)
-- case termTag val of
-- t | t == _ERA_ -> cont host (reduceDupEra term val)
-- t | t == _LAM_ -> cont host (reduceDupLam term val)
-- t | t == _SUP_ -> cont host (reduceDupSup term val)
-- t | t == _CTR_ -> cont host (reduceDupCtr term val)
-- t | t == _W32_ -> cont host (reduceDupW32 term val)
-- t | t == _CHR_ -> cont host (reduceDupW32 term val)
-- _ -> set (loc + 0) val >> return term
-- else do
-- set host (termRemBit sb1)
-- reduceAt debug book host
-- t | t == _VAR_ -> do
-- sub <- got (loc + 0)
-- if termGetBit sub == 0
-- then return term
-- else do
-- set host (termRemBit sub)
-- reduceAt debug book host
-- t | t == _REF_ -> do
-- reduceRefAt book host
-- reduceAt debug book host
-- _ -> do
-- return term
-- where
-- cont host action = do
-- ret <- action
-- set host ret
-- reduceAt debug book host
-- gotT :: Book -> Loc -> HVM Term
-- gotT book host = got host
-- reduceRefAt :: Book -> Loc -> HVM Term
-- reduceRefAt book host = do
-- term <- got host
-- let lab = termLab term
-- let loc = termLoc term
-- let fid = fromIntegral lab
-- let ari = funArity book fid
-- case lab of
-- x | x == _DUP_F_ -> reduceRefAt_DupF book host loc ari
-- x | x == _SUP_F_ -> reduceRefAt_SupF book host loc ari
-- x | x == _LOG_F_ -> reduceRefAt_LogF book host loc ari
-- otherwise -> case MS.lookup fid (fidToFun book) of
-- Just ((copy, args), core) -> do
-- incItr
-- when (length args /= fromIntegral ari) $ do
-- putStrLn $ "RUNTIME_ERROR: arity mismatch on call to '@" ++ mget (fidToNam book) fid ++ "'."
-- exitFailure
-- argTerms <- if ari == 0
-- then return []
-- else forM (zip [0..] args) $ \(i, (strict, _)) -> do
-- term <- got (loc + i)
-- if strict
-- then reduceAt False book (loc + i)
-- else return term
-- doInjectCoreAt book core host $ zip (map snd args) argTerms
-- Nothing -> do
-- putStrLn $ "RUNTIME_ERROR: Function ID " ++ show fid ++ " not found in fidToFun book."
-- exitFailure
-- -- Primitive: Dynamic Dup `@DUP(lab val λdp0λdp1(bod))`
-- reduceRefAt_DupF :: Book -> Loc -> Loc -> Word16 -> HVM Term
-- reduceRefAt_DupF book host loc ari = do
-- incItr
-- when (ari /= 3) $ do
-- putStrLn $ "RUNTIME_ERROR: arity mismatch on call to '@DUP'."
-- exitFailure
-- lab <- reduceAt False book (loc + 0)
-- val <- got (loc + 1)
-- bod <- got (loc + 2)
-- dup <- allocNode 1
-- case termTag lab of
-- t | t == _W32_ -> do
-- when (termLoc lab > 0xFFFFFF) $ do
-- error "RUNTIME_ERROR: dynamic DUP label too large"
-- -- Create the DUP node with value
-- set (dup + 0) val
-- -- Create first APP node for (APP bod DP0)
-- app1 <- allocNode 2
-- set (app1 + 0) bod
-- set (app1 + 1) (termNew _DP0_ (termLoc lab) dup)
-- -- Create second APP node for (APP (APP bod DP0) DP1)
-- app2 <- allocNode 2
-- set (app2 + 0) (termNew _APP_ 0 app1)
-- set (app2 + 1) (termNew _DP1_ (termLoc lab) dup)
-- let ret = termNew _APP_ 0 app2
-- set host ret
-- return ret
-- _ -> do
-- core <- doExtractCoreAt gotT book (loc + 0)
-- putStrLn $ "RUNTIME_ERROR: dynamic DUP without numeric label: " ++ showTerm lab
-- putStrLn $ showCore (doLiftDups core)
-- exitFailure
-- -- Primitive: Dynamic Sup `@SUP(lab tm0 tm1)`
-- reduceRefAt_SupF :: Book -> Loc -> Loc -> Word16 -> HVM Term
-- reduceRefAt_SupF book host loc ari = do
-- incItr
-- when (ari /= 3) $ do
-- putStrLn $ "RUNTIME_ERROR: arity mismatch on call to '@SUP'."
-- exitFailure
-- lab <- reduceAt False book (loc + 0)
-- tm0 <- got (loc + 1)
-- tm1 <- got (loc + 2)
-- sup <- allocNode 2
-- case termTag lab of
-- t | t == _W32_ -> do
-- when (termLoc lab > 0xFFFFFF) $ do
-- error "RUNTIME_ERROR: dynamic SUP label too large"
-- let ret = termNew _SUP_ (termLoc lab) sup
-- set (sup + 0) tm0
-- set (sup + 1) tm1
-- set host ret
-- return ret
-- _ -> error "RUNTIME_ERROR: dynamic SUP without numeric label."
-- -- Primitive: Logger `@LOG(msg)`
-- -- Will extract the term and log it.
-- -- Returns 0.
-- reduceRefAt_LogF :: Book -> Loc -> Loc -> Word16 -> HVM Term
-- reduceRefAt_LogF book host loc ari = do
-- incItr
-- when (ari /= 1) $ do
-- putStrLn $ "RUNTIME_ERROR: arity mismatch on call to '@LOG'."
-- exitFailure
-- msg <- doExtractCoreAt gotT book (loc + 0)
-- putStrLn $ showCore (doLiftDups msg)
-- -- msgs <- doCollapseFlatAt gotT book (loc + 0)
-- -- forM_ msgs $ \msg -> do
-- -- putStrLn $ showCore msg
-- let ret = termNew _W32_ 0 0
-- set host ret
-- return ret
-- -- Primitive: Fresh `@FRESH`
-- -- Returns a fresh dup label.
-- reduceRefAt_FreshF :: Book -> Loc -> Loc -> Word16 -> HVM Term
-- reduceRefAt_FreshF book host loc ari = do
-- incItr
-- when (ari /= 0) $ do
-- putStrLn $ "RUNTIME_ERROR: arity mismatch on call to '@Fresh'."
-- exitFailure
-- num <- fromIntegral <$> fresh
-- let ret = termNew _W32_ 0 num
-- set host ret
-- return ret
-- reduceCAt :: Bool -> (Book -> Loc -> HVM Term)
-- reduceCAt = \ _ _ host -> reduceAtC host
-- UPDATED:
module Reduce where
import Control.Monad (when, forM, forM_)
import Data.Word
import Collapse
import Extract
import Foreign
import Inject
import Type
import System.Exit
import qualified Data.Map.Strict as MS
reduceAt :: Bool -> Book -> Loc -> HVM Term
reduceAt debug book host = do
term <- got host
let tag = termTag term
let lab = termLab term
let loc = termLoc term
when debug $ do
root <- doExtractCoreAt gotT book 0
core <- doExtractCoreAt gotT book host
putStrLn $ "reduce: " ++ showTerm term
-- putStrLn $ "---------------- CORE: "
-- putStrLn $ showCore core
putStrLn $ "---------------- ROOT: "
putStrLn $ showCore (doLiftDups root)
case tag of
t | t == _LET_ -> do
case modeT lab of
LAZY -> do
val <- got (loc + 0)
cont host (reduceLet term val)
STRI -> do
val <- reduceAt debug book (loc + 0)
cont host (reduceLet term val)
t | t == _APP_ -> do
fun <- reduceAt debug book (loc + 0)
case termTag fun of
t | t == _ERA_ -> cont host (reduceAppEra term fun)
t | t == _LAM_ -> cont host (reduceAppLam term fun)
t | t == _SUP_ -> cont host (reduceAppSup term fun)
t | t == _NIL_ -> cont host (reduceAppNil term fun)
t | t == _TUP_ -> cont host (reduceAppTup term fun)
t | t == _W32_ -> cont host (reduceAppW32 term fun)
t | t == _SET_ -> cont host (reduceAppTyp term fun)
t | t == _EMP_ -> cont host (reduceAppTyp term fun)
t | t == _UNI_ -> cont host (reduceAppTyp term fun)
t | t == _U32_ -> cont host (reduceAppTyp term fun)
t | t == _SIG_ -> cont host (reduceAppTyp term fun)
t | t == _ALL_ -> cont host (reduceAppTyp term fun)
_ -> set (loc + 0) fun >> return term
t | t == _USE_ -> do
val <- reduceAt debug book (loc + 0)
case termTag val of
t | t == _ERA_ -> cont host (reduceUseEra term val)
t | t == _SUP_ -> cont host (reduceUseSup term val)
t | t == _LAM_ -> cont host (reduceUseLam term val)
t | t == _NIL_ -> cont host (reduceUseNil term val)
t | t == _TUP_ -> cont host (reduceUseTup term val)
t | t == _W32_ -> cont host (reduceUseW32 term val)
t | t == _SET_ -> cont host (reduceUseTyp term val)
t | t == _EMP_ -> cont host (reduceUseTyp term val)
t | t == _UNI_ -> cont host (reduceUseTyp term val)
t | t == _U32_ -> cont host (reduceUseTyp term val)
t | t == _SIG_ -> cont host (reduceUseTyp term val)
t | t == _ALL_ -> cont host (reduceUseTyp term val)
_ -> set (loc + 0) val >> return term
t | t == _GET_ -> do
val <- reduceAt debug book (loc + 0)
case termTag val of
t | t == _ERA_ -> cont host (reduceGetEra term val)
t | t == _SUP_ -> cont host (reduceGetSup term val)
t | t == _NIL_ -> cont host (reduceGetNil term val)
t | t == _TUP_ -> cont host (reduceGetTup term val)
t | t == _LAM_ -> cont host (reduceGetLam term val)
t | t == _W32_ -> cont host (reduceGetW32 term val)
t | t == _SET_ -> cont host (reduceGetTyp term val)
t | t == _EMP_ -> cont host (reduceGetTyp term val)
t | t == _UNI_ -> cont host (reduceGetTyp term val)
t | t == _U32_ -> cont host (reduceGetTyp term val)
t | t == _SIG_ -> cont host (reduceGetTyp term val)
t | t == _ALL_ -> cont host (reduceGetTyp term val)
_ -> set (loc + 0) val >> return term
t | t == _SWI_ -> do
val <- reduceAt debug book (loc + 0)
case termTag val of
t | t == _ERA_ -> cont host (reduceSwiEra term val)
t | t == _SUP_ -> cont host (reduceSwiSup term val)
t | t == _NIL_ -> cont host (reduceSwiNil term val)
t | t == _TUP_ -> cont host (reduceSwiTup term val)
t | t == _LAM_ -> cont host (reduceSwiLam term val)
t | t == _W32_ -> cont host (reduceSwiW32 term val)
t | t == _SET_ -> cont host (reduceSwiTyp term val)
t | t == _EMP_ -> cont host (reduceSwiTyp term val)
t | t == _UNI_ -> cont host (reduceSwiTyp term val)
t | t == _U32_ -> cont host (reduceSwiTyp term val)
t | t == _SIG_ -> cont host (reduceSwiTyp term val)
t | t == _ALL_ -> cont host (reduceSwiTyp term val)
_ -> set (loc + 0) val >> return term
t | t == _OPX_ -> do
val <- reduceAt debug book (loc + 0)
case termTag val of
t | t == _ERA_ -> cont host (reduceOpxEra term val)
t | t == _LAM_ -> cont host (reduceOpxLam term val)
t | t == _SUP_ -> cont host (reduceOpxSup term val)
t | t == _NIL_ -> cont host (reduceOpxNil term val)
t | t == _TUP_ -> cont host (reduceOpxTup term val)
t | t == _W32_ -> cont host (reduceOpxW32 term val)
t | t == _SET_ -> cont host (reduceOpxTyp term val)
t | t == _EMP_ -> cont host (reduceOpxTyp term val)
t | t == _UNI_ -> cont host (reduceOpxTyp term val)
t | t == _U32_ -> cont host (reduceOpxTyp term val)
t | t == _SIG_ -> cont host (reduceOpxTyp term val)
t | t == _ALL_ -> cont host (reduceOpxTyp term val)
_ -> set (loc + 0) val >> return term
t | t == _OPY_ -> do
val <- reduceAt debug book (loc + 0)
case termTag val of
t | t == _ERA_ -> cont host (reduceOpyEra term val)
t | t == _LAM_ -> cont host (reduceOpyLam term val)
t | t == _SUP_ -> cont host (reduceOpySup term val)
t | t == _NIL_ -> cont host (reduceOpyNil term val)
t | t == _TUP_ -> cont host (reduceOpyTup term val)
t | t == _W32_ -> cont host (reduceOpyW32 term val)
t | t == _SET_ -> cont host (reduceOpyTyp term val)
t | t == _EMP_ -> cont host (reduceOpyTyp term val)
t | t == _UNI_ -> cont host (reduceOpyTyp term val)
t | t == _U32_ -> cont host (reduceOpyTyp term val)
t | t == _SIG_ -> cont host (reduceOpyTyp term val)
t | t == _ALL_ -> cont host (reduceOpyTyp term val)
_ -> set (loc + 0) val >> return term
t | t == _DP0_ -> do
sb0 <- got (loc + 0)
if termGetBit sb0 == 0
then do
val <- reduceAt debug book (loc + 0)
case termTag val of
t | t == _ERA_ -> cont host (reduceDupEra term val)
t | t == _LAM_ -> cont host (reduceDupLam term val)
t | t == _SUP_ -> cont host (reduceDupSup term val)
t | t == _NIL_ -> cont host (reduceDupNil term val)
t | t == _TUP_ -> cont host (reduceDupTup term val)
t | t == _W32_ -> cont host (reduceDupW32 term val)
t | t == _SET_ -> cont host (reduceDupSet term val)
t | t == _EMP_ -> cont host (reduceDupEmp term val)
t | t == _UNI_ -> cont host (reduceDupUni term val)
t | t == _U32_ -> cont host (reduceDupU32 term val)
t | t == _SIG_ -> cont host (reduceDupSig term val)
t | t == _ALL_ -> cont host (reduceDupAll term val)
_ -> set (loc + 0) val >> return term
else do
set host (termRemBit sb0)
reduceAt debug book host
t | t == _DP1_ -> do
sb1 <- got (loc + 0)
if termGetBit sb1 == 0
then do
val <- reduceAt debug book (loc + 0)
case termTag val of
t | t == _ERA_ -> cont host (reduceDupEra term val)
t | t == _LAM_ -> cont host (reduceDupLam term val)
t | t == _SUP_ -> cont host (reduceDupSup term val)
t | t == _NIL_ -> cont host (reduceDupNil term val)
t | t == _TUP_ -> cont host (reduceDupTup term val)
t | t == _W32_ -> cont host (reduceDupW32 term val)
t | t == _SET_ -> cont host (reduceDupSet term val)
t | t == _EMP_ -> cont host (reduceDupEmp term val)
t | t == _UNI_ -> cont host (reduceDupUni term val)
t | t == _U32_ -> cont host (reduceDupU32 term val)
t | t == _SIG_ -> cont host (reduceDupSig term val)
t | t == _ALL_ -> cont host (reduceDupAll term val)
_ -> set (loc + 0) val >> return term
else do
set host (termRemBit sb1)
reduceAt debug book host
t | t == _VAR_ -> do
sub <- got (loc + 0)
if termGetBit sub == 0
then return term
else do
set host (termRemBit sub)
reduceAt debug book host
t | t == _REF_ -> do
reduceRefAt book host
reduceAt debug book host
_ -> do
return term
where
cont host action = do
ret <- action
set host ret
reduceAt debug book host
gotT :: Book -> Loc -> HVM Term
gotT book host = got host
reduceRefAt :: Book -> Loc -> HVM Term
reduceRefAt book host = do
term <- got host
let lab = termLab term
let loc = termLoc term
let fid = fromIntegral lab
let ari = funArity book fid
case lab of
x | x == _DUP_F_ -> reduceRefAt_DupF book host loc ari
x | x == _SUP_F_ -> reduceRefAt_SupF book host loc ari
x | x == _LOG_F_ -> reduceRefAt_LogF book host loc ari
otherwise -> case MS.lookup fid (fidToFun book) of
Just ((copy, args), core) -> do
incItr
when (length args /= fromIntegral ari) $ do
putStrLn $ "RUNTIME_ERROR: arity mismatch on call to '@" ++ mget (fidToNam book) fid ++ "'."
exitFailure
argTerms <- if ari == 0
then return []
else forM (zip [0..] args) $ \(i, (strict, _)) -> do
term <- got (loc + i)
if strict
then reduceAt False book (loc + i)
else return term
doInjectCoreAt book core host $ zip (map snd args) argTerms
Nothing -> do
putStrLn $ "RUNTIME_ERROR: Function ID " ++ show fid ++ " not found in fidToFun book."
exitFailure
-- Primitive: Dynamic Dup `@DUP(lab val λdp0λdp1(bod))`
reduceRefAt_DupF :: Book -> Loc -> Loc -> Word16 -> HVM Term
reduceRefAt_DupF book host loc ari = do
incItr
when (ari /= 3) $ do
putStrLn $ "RUNTIME_ERROR: arity mismatch on call to '@DUP'."
exitFailure
lab <- reduceAt False book (loc + 0)
val <- got (loc + 1)
bod <- got (loc + 2)
dup <- allocNode 1
case termTag lab of
t | t == _W32_ -> do
when (termLoc lab > 0xFFFFFF) $ do
error "RUNTIME_ERROR: dynamic DUP label too large"
-- Create the DUP node with value
set (dup + 0) val
-- Create first APP node for (APP bod DP0)
app1 <- allocNode 2
set (app1 + 0) bod
set (app1 + 1) (termNew _DP0_ (termLoc lab) dup)
-- Create second APP node for (APP (APP bod DP0) DP1)
app2 <- allocNode 2
set (app2 + 0) (termNew _APP_ 0 app1)
set (app2 + 1) (termNew _DP1_ (termLoc lab) dup)
let ret = termNew _APP_ 0 app2
set host ret
return ret
_ -> do
core <- doExtractCoreAt gotT book (loc + 0)
putStrLn $ "RUNTIME_ERROR: dynamic DUP without numeric label: " ++ showTerm lab
putStrLn $ showCore (doLiftDups core)
exitFailure
-- Primitive: Dynamic Sup `@SUP(lab tm0 tm1)`
reduceRefAt_SupF :: Book -> Loc -> Loc -> Word16 -> HVM Term
reduceRefAt_SupF book host loc ari = do
incItr
when (ari /= 3) $ do
putStrLn $ "RUNTIME_ERROR: arity mismatch on call to '@SUP'."
exitFailure
lab <- reduceAt False book (loc + 0)
tm0 <- got (loc + 1)
tm1 <- got (loc + 2)
sup <- allocNode 2
case termTag lab of
t | t == _W32_ -> do
when (termLoc lab > 0xFFFFFF) $ do
error "RUNTIME_ERROR: dynamic SUP label too large"
let ret = termNew _SUP_ (termLoc lab) sup
set (sup + 0) tm0
set (sup + 1) tm1
set host ret
return ret
_ -> error "RUNTIME_ERROR: dynamic SUP without numeric label."
-- Primitive: Logger `@LOG(msg)`
-- Will extract the term and log it.
-- Returns 0.
reduceRefAt_LogF :: Book -> Loc -> Loc -> Word16 -> HVM Term
reduceRefAt_LogF book host loc ari = do
incItr
when (ari /= 1) $ do
putStrLn $ "RUNTIME_ERROR: arity mismatch on call to '@LOG'."
exitFailure
msg <- doExtractCoreAt gotT book (loc + 0)
putStrLn $ showCore (doLiftDups msg)
-- msgs <- doCollapseFlatAt gotT book (loc + 0)
-- forM_ msgs $ \msg -> do
-- putStrLn $ showCore msg
let ret = termNew _W32_ 0 0
set host ret
return ret
-- Primitive: Fresh `@FRESH`
-- Returns a fresh dup label.
reduceRefAt_FreshF :: Book -> Loc -> Loc -> Word16 -> HVM Term
reduceRefAt_FreshF book host loc ari = do
incItr
when (ari /= 0) $ do
putStrLn $ "RUNTIME_ERROR: arity mismatch on call to '@Fresh'."
exitFailure
num <- fromIntegral <$> fresh
let ret = termNew _W32_ 0 num
set host ret
return ret
reduceCAt :: Bool -> Book -> Loc -> HVM Term
reduceCAt = \ _ _ host -> reduceAtC host
reduce :: Bool -> Book -> Term -> HVM Term
reduce debug book term = do
loc <- allocNode 1
set loc term
reduceAt debug book loc
{-./Type.hs-}
{-./Check.hs-}
-- [># LANGUAGE OverloadedStrings #<]
-- [># LANGUAGE TemplateHaskell #<]
-- module Main where
-- import Network.Socket as Network
-- import System.IO (hSetEncoding, utf8, hPutStrLn, stderr)
-- import Control.Exception (try, fromException, SomeException, finally, AsyncException(UserInterrupt))
-- import Control.Monad (guard, when, foldM, forM_, unless)
-- import Data.FileEmbed
-- import Data.List (partition, isPrefixOf, take, find)
-- import Data.Word
-- import Foreign.C.Types
-- import Foreign.LibFFI
-- import Foreign.LibFFI.Types
-- import GHC.Clock
-- import GHC.Conc
-- import Collapse
-- import Compile
-- import Extract
-- import Foreign
-- import Inject
-- import Parse
-- import Reduce
-- import Type
-- import System.Environment (getArgs)
-- import System.Exit (exitWith, ExitCode(ExitSuccess, ExitFailure))
-- import System.IO
-- import System.IO (readFile')
-- import System.IO.Unsafe (unsafePerformIO)
-- import System.IO.Error (tryIOError)
-- import System.Posix.DynamicLinker
-- import System.Process (callCommand)
-- import Text.Printf
-- import Data.IORef
-- import qualified Data.Map.Strict as MS
-- import Text.Read (readMaybe)
-- runtime_c :: String
-- runtime_c = $(embedStringFile "./src/Runtime.c")
-- -- Main
-- -- ----
-- data RunMode
-- = Normalize
-- | Collapse (Maybe Int)
-- deriving Eq
-- main :: IO ()
-- main = do
-- args <- getArgs
-- result <- case args of
-- ("run" : file : rest) -> do
-- let (flags, sArgs) = partition ("-" `isPrefixOf`) rest
-- let compiled = "-c" `elem` flags
-- let collapseFlag = Data.List.find (isPrefixOf "-C") flags >>= parseCollapseFlag
-- let stats = "-s" `elem` flags
-- let debug = "-d" `elem` flags
-- let hideQuotes = "-Q" `elem` flags
-- let mode = case collapseFlag of { Just n -> Collapse n ; Nothing -> Normalize }
-- cliRun file debug compiled mode stats hideQuotes sArgs
-- ("serve" : file : rest) -> do
-- let (flags, _) = partition ("-" `isPrefixOf`) rest
-- let compiled = "-c" `elem` flags
-- let collapseFlag = Data.List.find (isPrefixOf "-C") flags >>= parseCollapseFlag
-- let stats = "-s" `elem` flags
-- let debug = "-d" `elem` flags
-- let hideQuotes = "-Q" `elem` flags
-- let mode = case collapseFlag of { Just n -> Collapse n ; Nothing -> Normalize }
-- cliServe file debug compiled mode stats hideQuotes
-- ["help"] -> printHelp
-- _ -> printHelp
-- case result of
-- Left err -> do
-- putStrLn err
-- exitWith (ExitFailure 1)
-- Right _ -> do
-- exitWith ExitSuccess
-- parseCollapseFlag :: String -> Maybe (Maybe Int)
-- parseCollapseFlag ('-':'C':rest) =
-- case rest of
-- "" -> Just Nothing
-- n -> Just (readMaybe n)
-- parseCollapseFlag _ = Nothing
-- printHelp :: IO (Either String ())
-- printHelp = do
-- putStrLn "HVM usage:"
-- putStrLn " hvm help # Shows this help message"
-- putStrLn " hvm run <file> [flags] [args...] # Evals main"
-- putStrLn " hvm serve <file> [flags] # Starts socket server on port 8080"
-- putStrLn " -c # Runs with compiled mode (fast)"
-- putStrLn " -C # Collapse the result to a list of λ-Terms"
-- putStrLn " -CN # Same as above, but show only first N results"
-- putStrLn " -s # Show statistics"
-- putStrLn " -d # Print execution steps (debug mode)"
-- putStrLn " -Q # Hide quotes in output"
-- return $ Right ()
-- -- CLI Commands
-- -- ------------
-- cliRun :: FilePath -> Bool -> Bool -> RunMode -> Bool -> Bool -> [String] -> IO (Either String ())
-- cliRun filePath debug compiled mode showStats hideQuotes strArgs = do
-- book <- loadBook filePath compiled
-- -- Abort when main isn't present
-- when (not $ MS.member "main" (namToFid book)) $ do
-- putStrLn "Error: 'main' not found."
-- exitWith (ExitFailure 1)
-- -- Abort when wrong number of strArgs
-- let ((_, mainArgs), _) = mget (fidToFun book) (mget (namToFid book) "main")
-- when (length strArgs /= length mainArgs) $ do
-- putStrLn $ "Error: 'main' expects " ++ show (length mainArgs)
-- ++ " arguments, found " ++ show (length strArgs)
-- exitWith (ExitFailure 1)
-- -- Normalize main
-- init <- getMonotonicTimeNSec
-- root <- injectMain book strArgs
-- rxAt <- if compiled
-- then return (reduceCAt debug)
-- else return (reduceAt debug)
-- vals <- case mode of
-- Collapse _ -> doCollapseFlatAt rxAt book 0
-- Normalize -> do
-- core <- doExtractCoreAt rxAt book 0
-- return [(doLiftDups core)]
-- -- Print results
-- case mode of
-- Collapse limit -> do
-- lastItrs <- newIORef 0
-- let limitedVals = maybe id Data.List.take limit vals
-- forM_ limitedVals $ \ term -> do
-- currItrs <- getItr
-- prevItrs <- readIORef lastItrs
-- let output = if hideQuotes then removeQuotes (show term) else show term
-- printf "%s\n" output
-- writeIORef lastItrs currItrs
-- putStrLn ""
-- Normalize -> do
-- let output = if hideQuotes
-- then removeQuotes (show (head vals))
-- else show (head vals)
-- putStrLn output
-- -- Prints total time
-- end <- getMonotonicTimeNSec
-- -- Show stats
-- when showStats $ do
-- itrs <- getItr
-- size <- getLen
-- let time = fromIntegral (end - init) / (10^9) :: Double
-- let mips = (fromIntegral itrs / 1000000.0) / time
-- printf "WORK: %llu interactions\n" itrs
-- printf "TIME: %.7f seconds\n" time
-- printf "SIZE: %llu nodes\n" size
-- printf "PERF: %.3f MIPS\n" mips
-- return ()
-- -- Finalize
-- hvmFree
-- return $ Right ()
-- cliServe :: FilePath -> Bool -> Bool -> RunMode -> Bool -> Bool -> IO (Either String ())
-- cliServe filePath debug compiled mode showStats hideQuotes = do
-- book <- loadBook filePath compiled
-- -- Abort when main isn't present
-- when (not $ MS.member "main" (namToFid book)) $ do
-- putStrLn "Error: 'main' not found."
-- exitWith (ExitFailure 1)
-- putStrLn "HVM serve mode. Listening on port 8080."
-- sock <- socket AF_INET Stream 0
-- setSocketOption sock ReuseAddr 1
-- Network.bind sock (SockAddrInet 8080 0)
-- listen sock 5
-- putStrLn "Server started. Listening on port 8080."
-- serverLoop sock book `finally` do
-- close sock
-- hvmFree
-- putStrLn "\nServer terminated."
-- return $ Right ()
-- where
-- serverLoop sock book = do
-- result <- try $ do
-- (conn, _) <- accept sock
-- h <- socketToHandle conn ReadWriteMode
-- hSetBuffering h LineBuffering
-- hSetEncoding h utf8
-- input <- hGetLine h
-- unless (input == "exit" || input == "quit") $ do
-- oldSize <- getLen
-- root <- injectMain book [input]
-- rxAt <- if compiled then return (reduceCAt debug) else return (reduceAt debug)
-- vals <- case mode of
-- Collapse _ -> doCollapseFlatAt rxAt book 0
-- Normalize -> do
-- core <- doExtractCoreAt rxAt book 0
-- return [doLiftDups core]
-- let output = case mode of
-- Collapse limit -> do
-- let limitedVals = maybe id Data.List.take limit vals
-- let outputs = map (\term -> if hideQuotes then removeQuotes (show term) else show term) limitedVals
-- unlines outputs
-- Normalize -> do
-- let result = head vals
-- if hideQuotes then removeQuotes (show result) else show result
-- hPutStrLn h output
-- setLen oldSize
-- when showStats $ do
-- itrs <- getItr
-- size <- getLen
-- hPutStrLn h $ "WORK: " ++ show itrs ++ " interactions"
-- hPutStrLn h $ "SIZE: " ++ show size ++ " nodes"
-- setItr 0
-- hClose h
-- case result of
-- Left e -> case fromException e of
-- Just UserInterrupt -> return () -- Exit loop on Ctrl+C
-- _ -> do
-- hPutStrLn stderr $ "Connection error: " ++ show (e :: SomeException)
-- serverLoop sock book
-- Right _ -> serverLoop sock book
-- -- Load and initialize a book from a file
-- loadBook :: FilePath -> Bool -> IO Book
-- loadBook filePath compiled = do
-- hvmInit
-- code <- readFile' filePath
-- book <- doParseBook filePath code
-- forM_ (MS.toList (cidToAri book)) $ \ (cid, ari) -> do
-- hvmSetCari cid (fromIntegral ari)
-- forM_ (MS.toList (cidToLen book)) $ \ (cid, len) -> do
-- hvmSetClen cid (fromIntegral len)
-- forM_ (MS.toList (cidToADT book)) $ \ (cid, adt) -> do
-- hvmSetCadt cid (fromIntegral adt)
-- forM_ (MS.toList (fidToFun book)) $ \ (fid, ((_, args), _)) -> do
-- hvmSetFari fid (fromIntegral $ length args)
-- when compiled $ do
-- let decls = compileHeaders book
-- let funcs = map (\ (fid, _) -> compile book fid) (MS.toList (fidToFun book))
-- let mainC = unlines $ [runtime_c] ++ [decls] ++ funcs ++ [genMain book]
-- -- Try to use a cached .so file
-- callCommand "mkdir -p .build"
-- let fName = last $ words $ map (\c -> if c == '/' then ' ' else c) filePath
-- let cPath = ".build/" ++ fName ++ ".c"
-- let oPath = ".build/" ++ fName ++ ".so"
-- oldCFile <- tryIOError (readFile' cPath)
-- bookLib <- if oldCFile == Right mainC then do
-- dlopen oPath [RTLD_NOW]
-- else do
-- writeFile cPath mainC
-- callCommand $ "gcc -O2 -fPIC -shared " ++ cPath ++ " -o " ++ oPath
-- dlopen oPath [RTLD_NOW]
-- forM_ (MS.keys (fidToFun book)) $ \ fid -> do
-- funPtr <- dlsym bookLib (mget (fidToNam book) fid ++ "_f")
-- hvmDefine fid funPtr
-- -- Link compiled state
-- hvmGotState <- hvmGetState
-- hvmSetState <- dlsym bookLib "hvm_set_state"
-- callFFI hvmSetState retVoid [argPtr hvmGotState]
-- return book
-- genMain :: Book -> String
-- genMain book =
-- let mainFid = mget (namToFid book) "main"
-- registerFuncs = unlines [" hvm_define(" ++ show fid ++ ", " ++ mget (fidToNam book) fid ++ "_f);" | fid <- MS.keys (fidToFun book)]
-- in unlines
-- [ "int main() {"
-- , " hvm_init();"
-- , registerFuncs
-- , " clock_t start = clock();"
-- , " Term root = term_new(REF, "++show mainFid++", 0);"
-- , " normal(root);"
-- , " double time = (double)(clock() - start) / CLOCKS_PER_SEC * 1000;"
-- , " printf(\"WORK: %\"PRIu64\" interactions\\n\", get_itr());"
-- , " printf(\"TIME: %.3fs seconds\\n\", time / 1000.0);"
-- , " printf(\"SIZE: %llu nodes\\n\", get_len());"
-- , " printf(\"PERF: %.3f MIPS\\n\", (get_itr() / 1000000.0) / (time / 1000.0));"
-- , " hvm_free();"
-- , " return 0;"
-- , "}"
-- ]
-- -- Parse arguments and create a term with the given function name
-- injectMain :: Book -> [String] -> IO Term
-- injectMain book args = do
-- (book, parsedArgs) <- foldM (\(b, as) str -> do
-- (b', a) <- doParseArgument str b
-- return (b', a:as)) (book, []) args
-- let fid = mget (namToFid book) "main"
-- let main = Ref "main" fid (reverse parsedArgs)
-- root <- doInjectCoreAt book main 0 []
-- return root
-- removeQuotes :: String -> String
-- removeQuotes s = case s of
-- '"':rest -> init rest -- Remove first and last quote if present
-- _ -> s -- Otherwise return as-is
-- TASK: update Main.hs fully
-- COMPLETE UPDATED MAIN.HS:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Main where
import Network.Socket as Network
import System.IO (hSetEncoding, utf8, hPutStrLn, stderr)
import Control.Exception (try, fromException, SomeException, finally, AsyncException(UserInterrupt))
import Control.Monad (guard, when, foldM, forM_, unless)
import Data.FileEmbed
import Data.List (partition, isPrefixOf, take, find)
import Data.Word
import Foreign.C.Types
import Foreign.LibFFI
import Foreign.LibFFI.Types
import GHC.Clock
import GHC.Conc
import Check
import Collapse
import Compile
import Extract
import Foreign
import Inject
import Parse
import Reduce
import Type
import System.Environment (getArgs)
import System.Exit (exitWith, ExitCode(ExitSuccess, ExitFailure))
import System.IO
import System.IO (readFile')
import System.IO.Unsafe (unsafePerformIO)
import System.IO.Error (tryIOError)
import System.Posix.DynamicLinker
import System.Process (callCommand)
import Text.Printf
import Data.IORef
import qualified Data.Map.Strict as MS
import Text.Read (readMaybe)
runtime_c :: String
runtime_c = $(embedStringFile "./src/Runtime.c")
-- Main
-- ----
data RunMode
= Normalize
| Collapse (Maybe Int)
deriving Eq
main :: IO ()
main = do
args <- getArgs
result <- case args of
("run" : file : rest) -> do
let (flags, sArgs) = partition ("-" `isPrefixOf`) rest
let compiled = "-c" `elem` flags
let collapseFlag = Data.List.find (isPrefixOf "-C") flags >>= parseCollapseFlag
let stats = "-s" `elem` flags
let debug = "-d" `elem` flags
let hideQuotes = "-Q" `elem` flags
let mode = case collapseFlag of { Just n -> Collapse n ; Nothing -> Normalize }
cliRun file debug compiled mode stats hideQuotes sArgs
("serve" : file : rest) -> do
let (flags, _) = partition ("-" `isPrefixOf`) rest
let compiled = "-c" `elem` flags
let collapseFlag = Data.List.find (isPrefixOf "-C") flags >>= parseCollapseFlag
let stats = "-s" `elem` flags
let debug = "-d" `elem` flags
let hideQuotes = "-Q" `elem` flags
let mode = case collapseFlag of { Just n -> Collapse n ; Nothing -> Normalize }
cliServe file debug compiled mode stats hideQuotes
["help"] -> printHelp
_ -> printHelp
case result of
Left err -> do
putStrLn err
exitWith (ExitFailure 1)
Right _ -> do
exitWith ExitSuccess
parseCollapseFlag :: String -> Maybe (Maybe Int)
parseCollapseFlag ('-':'C':rest) =
case rest of
"" -> Just Nothing
n -> Just (readMaybe n)
parseCollapseFlag _ = Nothing
printHelp :: IO (Either String ())
printHelp = do
putStrLn "HVM usage:"
putStrLn " hvm help # Shows this help message"
putStrLn " hvm run <file> [flags] [args...] # Evals main"
putStrLn " hvm serve <file> [flags] # Starts socket server on port 8080"
putStrLn " -c # Runs with compiled mode (fast)"
putStrLn " -C # Collapse the result to a list of λ-Terms"
putStrLn " -CN # Same as above, but show only first N results"
putStrLn " -s # Show statistics"
putStrLn " -d # Print execution steps (debug mode)"
putStrLn " -Q # Hide quotes in output"
return $ Right ()
-- CLI Commands
-- ------------
cliRun :: FilePath -> Bool -> Bool -> RunMode -> Bool -> Bool -> [String] -> IO (Either String ())
cliRun filePath debug compiled mode showStats hideQuotes strArgs = do
book <- loadBook filePath compiled
-- Abort when main isn't present
when (not $ MS.member "main" (namToFid book)) $ do
putStrLn "Error: 'main' not found."
exitWith (ExitFailure 1)
-- Abort when wrong number of strArgs
let ((_, mainArgs), _) = mget (fidToFun book) (mget (namToFid book) "main")
when (length strArgs /= length mainArgs) $ do
putStrLn $ "Error: 'main' expects " ++ show (length mainArgs)
++ " arguments, found " ++ show (length strArgs)
exitWith (ExitFailure 1)
-- Normalize main
init <- getMonotonicTimeNSec
root <- injectMain book strArgs
rxAt <- if compiled
then return (reduceCAt debug)
else return (reduceAt debug)
vals <- case mode of
Collapse _ -> doCollapseFlatAt rxAt book 0
Normalize -> do
core <- doExtractCoreAt rxAt book 0
return [(doLiftDups core)]
-- Print results
case mode of
Collapse limit -> do
lastItrs <- newIORef 0
let limitedVals = maybe id Data.List.take limit vals
forM_ limitedVals $ \ term -> do
currItrs <- getItr
prevItrs <- readIORef lastItrs
let output = if hideQuotes then removeQuotes (show term) else show term
printf "%s\n" output
writeIORef lastItrs currItrs
putStrLn ""
Normalize -> do
let output = if hideQuotes
then removeQuotes (show (head vals))
else show (head vals)
putStrLn output
-- Type check all nullary functions with annotated bodies
forM_ (MS.toList (fidToFun book)) $ \ (fid, ((_, args), body)) -> do
when (null args) $ do
case body of
Ann _ term typ -> do
printf "Checking @%s...\n" (mget (fidToNam book) fid)
hFlush stdout
-- Inject the term and type into fresh locations
termLoc <- allocNode 1
typeLoc <- allocNode 1
termTerm <- doInjectCoreAt book term termLoc []
typeTerm <- doInjectCoreAt book typ typeLoc []
-- Run the type check and store in a fresh location
result <- check 0 termTerm typeTerm
resultLoc <- allocNode 1
set resultLoc result
-- Collapse and print the result
collapsed <- doCollapseFlatAt rxAt book resultLoc
-- use unicode check / X symbols
if length collapsed > 0
then putStrLn "✓"
else putStrLn "✗"
_ -> return ()
-- Prints total time
end <- getMonotonicTimeNSec
-- Show stats
when showStats $ do
itrs <- getItr
size <- getLen
let time = fromIntegral (end - init) / (10^9) :: Double
let mips = (fromIntegral itrs / 1000000.0) / time
printf "WORK: %llu interactions\n" itrs
printf "TIME: %.7f seconds\n" time
printf "SIZE: %llu nodes\n" size
printf "PERF: %.3f MIPS\n" mips
return ()
-- Finalize
hvmFree
return $ Right ()
cliServe :: FilePath -> Bool -> Bool -> RunMode -> Bool -> Bool -> IO (Either String ())
cliServe filePath debug compiled mode showStats hideQuotes = do
book <- loadBook filePath compiled
-- Abort when main isn't present
when (not $ MS.member "main" (namToFid book)) $ do
putStrLn "Error: 'main' not found."
exitWith (ExitFailure 1)
putStrLn "HVM serve mode. Listening on port 8080."
sock <- socket AF_INET Stream 0
setSocketOption sock ReuseAddr 1
Network.bind sock (SockAddrInet 8080 0)
listen sock 5
putStrLn "Server started. Listening on port 8080."
serverLoop sock book `finally` do
close sock
hvmFree
putStrLn "\nServer terminated."
return $ Right ()
where
serverLoop sock book = do
result <- try $ do
(conn, _) <- accept sock
h <- socketToHandle conn ReadWriteMode
hSetBuffering h LineBuffering
hSetEncoding h utf8
input <- hGetLine h
unless (input == "exit" || input == "quit") $ do
oldSize <- getLen
root <- injectMain book [input]
rxAt <- if compiled then return (reduceCAt debug) else return (reduceAt debug)
vals <- case mode of
Collapse _ -> doCollapseFlatAt rxAt book 0
Normalize -> do
core <- doExtractCoreAt rxAt book 0
return [doLiftDups core]
let output = case mode of
Collapse limit -> do
let limitedVals = maybe id Data.List.take limit vals
let outputs = map (\term -> if hideQuotes then removeQuotes (show term) else show term) limitedVals
unlines outputs
Normalize -> do
let result = head vals
if hideQuotes then removeQuotes (show result) else show result
hPutStrLn h output
setLen oldSize
when showStats $ do
itrs <- getItr
size <- getLen
hPutStrLn h $ "WORK: " ++ show itrs ++ " interactions"
hPutStrLn h $ "SIZE: " ++ show size ++ " nodes"
setItr 0
hClose h
case result of
Left e -> case fromException e of
Just UserInterrupt -> return () -- Exit loop on Ctrl+C
_ -> do
hPutStrLn stderr $ "Connection error: " ++ show (e :: SomeException)
serverLoop sock book
Right _ -> serverLoop sock book
-- Load and initialize a book from a file
loadBook :: FilePath -> Bool -> IO Book
loadBook filePath compiled = do
hvmInit
code <- readFile' filePath
book <- doParseBook filePath code
forM_ (MS.toList (fidToFun book)) $ \ (fid, ((_, args), _)) -> do
hvmSetFari fid (fromIntegral $ length args)
when compiled $ do
let decls = compileHeaders book
let funcs = map (\ (fid, _) -> compile book fid) (MS.toList (fidToFun book))
let mainC = unlines $ [runtime_c] ++ [decls] ++ funcs ++ [genMain book]
-- Try to use a cached .so file
callCommand "mkdir -p .build"
let fName = last $ words $ map (\c -> if c == '/' then ' ' else c) filePath
let cPath = ".build/" ++ fName ++ ".c"
let oPath = ".build/" ++ fName ++ ".so"
oldCFile <- tryIOError (readFile' cPath)
bookLib <- if oldCFile == Right mainC then do
dlopen oPath [RTLD_NOW]
else do
writeFile cPath mainC
callCommand $ "gcc -O2 -fPIC -shared " ++ cPath ++ " -o " ++ oPath
dlopen oPath [RTLD_NOW]
forM_ (MS.keys (fidToFun book)) $ \ fid -> do
funPtr <- dlsym bookLib (mget (fidToNam book) fid ++ "_f")
hvmDefine fid funPtr
-- Link compiled state
hvmGotState <- hvmGetState
hvmSetState <- dlsym bookLib "hvm_set_state"
callFFI hvmSetState retVoid [argPtr hvmGotState]
return book
genMain :: Book -> String
genMain book =
let mainFid = mget (namToFid book) "main"
registerFuncs = unlines [" hvm_define(" ++ show fid ++ ", " ++ mget (fidToNam book) fid ++ "_f);" | fid <- MS.keys (fidToFun book)]
in unlines
[ "int main() {"
, " hvm_init();"
, registerFuncs
, " clock_t start = clock();"
, " Term root = term_new(REF, "++show mainFid++", 0);"
, " normal(root);"
, " double time = (double)(clock() - start) / CLOCKS_PER_SEC * 1000;"
, " printf(\"WORK: %\"PRIu64\" interactions\\n\", get_itr());"
, " printf(\"TIME: %.3fs seconds\\n\", time / 1000.0);"
, " printf(\"SIZE: %llu nodes\\n\", get_len());"
, " printf(\"PERF: %.3f MIPS\\n\", (get_itr() / 1000000.0) / (time / 1000.0));"
, " hvm_free();"
, " return 0;"
, "}"
]
-- Parse arguments and create a term with the given function name
injectMain :: Book -> [String] -> IO Term
injectMain book args = do
(book, parsedArgs) <- foldM (\(b, as) str -> do
(b', a) <- doParseArgument str b
return (b', a:as)) (book, []) args
let fid = mget (namToFid book) "main"
let main = Ref "main" fid (reverse parsedArgs)
root <- doInjectCoreAt book main 0 []
return root
removeQuotes :: String -> String
removeQuotes s = case s of
'"':rest -> init rest -- Remove first and last quote if present
_ -> s -- Otherwise return as-is
-- TASK: write below a complete bidirectional type checker, based on OldCheck
-- the types must be:
-- infer : Int → Term → IO (Result Term)
-- check : Int → Term → Term → IO (Result ())
-- make it a 1-to-1 correspondence to OldCheck
-- (omitted)
-- this is a good start, but:
-- - 1. the style of this file doesn't match the rest of the codebase. refactor its style completely.
-- - 2. many infer/check cases are missing. remember to consult the original OldCheck file to learn how each case should work. this file should remain a close correspondence to it.
-- rewrite the file again, addressing these issues:
-- (omitted)
-- the file above is better, but it still has some issues.
-- first: `termEqual` must be renamed to just `equal`
-- second: due to how superpositions work on HVM, we need to update the `equal`
-- function to handle SUPs, as follows:
-- &L{a0,a1} == b
-- ----------------------- EQUAL-SUP-VAL
-- ! &L{b0,b1} = b;
-- &L{a0 == a1 , b0 == b1}
-- a == &L{b0,b1}
-- ----------------------- EQUAL-VAL-SUP
-- ! &L{a0,a1} = a;
-- &L{a0 == a1 , b0 == b1}
-- Now, since EQL can now return a SUP, its return type can't be Bool anymore - it has to be Term.
-- As such, we will return `()` (One) when the terms are equal, and `*` (Era) otherwise.
-- Similarly, the infer and check functions must be updated:
-- infer d &L{x0,x1}
-- --------------------------- INFER-SUP
-- &L{infer d x0 , infer d x1}
-- check d &L{x0,x1} t
-- ------------------------------- CHECK-SUP-VAL
-- &L{check d x0 t , check d x1 t}
-- check d x &L{t0,t1}
-- ------------------------------- CHECK-SUP-VAL
-- &L{check d x t0 , check d x t1}
-- These interactions should be incorporated into equal, infer and check, directly.
-- That is, when a term is a SUP, we must return a SUP of both recursive calls.
-- Note that, since infer/equal can also return SUPs, their return can't be Result anymore.
-- Instead, their type will be:
-- infer :: Int -> Term -> IO Term
-- check :: Int -> Term -> Term -> IO Term
-- infer and check will simply return the type (when well-typed) or `*` (Era) otherwise.
-- check will either return `()` (One) (when well-typed), or `*` (Era) otherwise.
-- to report errors, we will just use a `print` (not an Error type)
-- Finally, since `equal` returns a Term rather than a Bool, we must also properly
-- match on its return, and dispatch SUP interactions accordingly. for better style,
-- create an auxiliary `validate` function that will do that part.
-- Also, note there is a logic error in your code.
-- The check function does NOT call whnf on the term, only on the goal.
-- Why did you invent that whnf call out of nowhere?
-- Double check the rest of the code to avoid errors like that.
-- Also, this style is terrible:
-- foo :: ...
-- foo ... x ... = do
-- x_nf <- whnf x
-- let xTag = tTag x_nf
-- case () of
-- _ | xTag == _XYZ_ -> ...
--
-- Instead, let's do it as follows:
-- 1. *BEFORE* calling foo, we take the whnf of the
-- 2. Then, we write foo like:
-- foo :: ...
-- foo ... x ...
-- | tTag x == _XYZ = ...
-- | ...
--
-- use this style in ALL functions, including equal.
-- NEVER use case () again.
-- Also, the following style is terrible:
-- foo = pure Something
-- Instead, let's ALWAYS use a do block, like:
-- foo = do
-- pure Something
-- Also, in many cases, you're allocating nodes like:
-- sup <- allocNode 2
-- set (sup + 0) dp0
-- set (sup + 1) dp1
-- That is very repetitive and error prone. Instead, create a function:
-- makeNode TAG LAB [children]
-- And use it through your code.
-- Complete updated file:
-- (omitted)
-- this initial implementation is good. there is a problem, though.
-- it is NOT exactly aligned with the OldCheck file.
-- for example, on OldCheck, the LAM-ALL case is:
-- #LAM{f}: ~ goal !f {
-- #ALL{A B}:
-- ! &{A0 A1}=A
-- ~ @check((+ dep 1) (f #ANN{#VAR{dep} A0}) #APP{B #ANN{#VAR{dep} A1}}) {
-- #Done{_}: #Done{1}
-- #Fail{e}: #Fail{e}
-- }
-- goal: #Fail{#TypeMismatch{#LAM{f} #ALL{#VAR{0} #LAM{λx.#VAR{0}}} goal}}
-- }
-- yet, on this file, it is:
-- | tTag tm == _LAM_ && tTag goal == _ALL_ = do
-- bod <- field tm 0
-- aTy <- field goal 0
-- bTy <- field goal 1
-- var <- makeNode _VAR_ (fromIntegral dep) [eraT]
-- app <- makeNode _APP_ 0 [bTy, var]
-- bTy <- whnf app
-- bOk <- checkGo (dep+1) bod bTy
-- if tTag bOk == _ERA_
-- then pure eraT
-- else pure nilT
-- problems:
-- - 'tm' should be named 'term', since that's how it is on OldCheck
-- - we should first match '_LAM_', then, inside it, match '_ALL_', preserving the OldCheck structure
-- - the body check must be `check (dep+1) (App f (Ann (Var dep) A0) ) (App B (Ann (Var dep) A1))`,
-- where `A0` and `A1` are cloned variables (with a fresh `DUP` node). instead, what you have is:
-- `check (dep+1) f (App B (Var dep))`, which is absolutely NOT the same.
-- - across the implementation, there are many instances like this one, where the new checker is not
-- a 1-to-1 correspondence with the old one, as it was supposed to be.
-- your goal is to rewrite Checker.hs again, from scratch, in order to address this issue.
-- make sure to consult OldCheck to learn how the infer/check functions should be structured.
-- remember that equal, both are already correct - no need to change them.
-- write the updated, complete Check.hs file below:
-- (omitted)
-- in the code above, you're using 'ann' more than once. this is NOT ALLOWED.
-- HVM is linear! instead, what you need to do is: use a DUP node to clone
-- 'a', and then make separate ANN's. again, notice how, on OldCheck, we do:
-- ! &{A0 A1}=A
-- this creates a DUP NODE that CLONES 'A' as 'A0' and 'A1'.
-- you're struggling very hard with linearity, so, please, reason about it.
-- then, write below the FIXED version of every defective case:
-- (omitted)
-- OK, this approach does look sensible. now, let's apply this idea to the
-- entire code, in the format we used previously. reimplement the entire file,
-- from scratch, to include the new logic. remember that you ALWAYS need to dup
-- a variable that you use more than once!
-- also, in your reimplementation, attempt to be more consistent with variable
-- names and alignment. for example, this is not fine:
-- lam <- makeNode _LAM_ 0 [setT]
-- allSet <- makeNode _ALL_ 0 [setT, lam]
-- because 'lam' and 'allSet' don't align.
-- now, proceed to rewrite the COMPLETE Check.hs file below:
-- also:
-- 1. for some reason, 'infer' and 'check' are NOT in the proposed structure.
-- infer has been written like:
-- infer dep term0 = do
-- term <- whnf term0
-- case tTag term of
-- t | t == _XYZ_ -> do
-- ...
-- while the correct structure is:
-- infer dep term0 = ... take whnf then call inferGo ...
-- ...
-- inferGo dep tm
-- | tTag tm == _XYZ_ = do
-- ...
-- the same is true for 'check'.
-- also, matching on SUP should NOT be separated from the other cases.
-- that makes absolutely no sense. do not do that.
-- complete, fixed file below:
-- (omitted)
-- ok, this looks great. now, review the code above and:
-- - 1. check if there is still any occurrence of a HVM-side Term being accidentally cloned in Haskell (i.e., by using its variable more than once)
-- - 2. review the code for bugs and issues in general
-- report below all your findings, in English:
-- • The helper dup2 is only creating the two DP-links; it never allocates the
-- actual _DUP_ node that owns them, so the run-time sharing discipline is
-- broken.
-- • dup2 always returns the fixed label 1 (getLabAuto is a stub). Every call
-- therefore re-uses the same label, violating the “fresh label per DUP”
-- rule.
-- • In inferGo (_SIG_, _APP_) and checkGo (_SIG_), the argument a/x is used
-- once before the call to dup2 and then twice inside the DP0/DP1 links,
-- yielding three usages of the same linear value. The DUP must be performed
-- before the very first consumption, not afterwards.
-- • both and equalGo forward the term b (or a) to two recursive calls without
-- duplicating it, so the same run-time pointer is consumed twice.
-- • In the _LAM_ ▸ _ALL_ rule we create two fresh variables (var0 / var1) but
-- use the original type a three times (once for check, twice in ANN) after a
-- single dup2; same linearity issue.
-- • Node construction for DP0/DP1 uses makeNode, but the tag set should be
-- _VAR_ when exposing the binder to the body; DP0/DP1 must live only inside
-- the DUP structure.
-- • Shadowing: in the _APP_ branch new bindings named “a” and “b” override the
-- outer ones, which is confusing and error-prone.
-- NOTE: no - this is actually fine and desirable. use shadowing whenever
-- possible. avoid prefixing names with ' and similar symbols.
-- • getLabAuto is only a placeholder; without a real fresh-label mechanism the
-- interpreter will emit duplicated labels and break reductions that depend
-- on label uniqueness.
-- let's fix this by importing the `book` in ALL functions (both, etc.)
-- • makeNode is used to create DP0 / DP1 links with a child list of length 1;
-- but a DP-link is *not* an allocated node: it is supposed to be a leaf term
-- (termNew tag lab loc=0). Allocating memory for them wastes space.
-- NOTE: that's correct. DUP nodes are allocated and store the duplicated term,
-- but the DP0/DP1 terms are leaves.
-- • Several inference branches (_REF_, _USE_, _GET_, _SWI_, …) still return
-- eraT after printing a message, whereas OldCheck propagated a #Fail
-- containing a structured error; the new checker therefore loses error
-- information.
-- NOTE: that's okay for now
-- • equalGo / both treat NIL as the only One value. They should also return a
-- SUP-tree when either side is a SUP of Ones, exactly like the other rules.
-- NOTE: what do you mean by that? I'm confused.
-- Fixing the points above requires:
-- – Implementing a real fresh-label supply and wrapping DP0/DP1 inside a
-- proper _DUP_ node.
-- – Performing DUP *before* the first duplicated use.
-- – Never forwarding the same Term to two different consumers without a DUP.
-- – Removing the stray debugging prints or turning them into proper error
-- nodes.
-- – Cleaning up variable shadowing and always using fresh names.
-- TASK: rewrite Check.hs from scratch, again, in order to address these issues.
-- Complete refactored Check.hs file. Remember to increment the fresh label.
-- Write the complete, final version below:
-- COMPLETE CHECK.HS:
-- (omitted)
-- this is still wrong. note that on rules like:
-- | tTag a == _SUP_ = do
-- a0 <- field a 0
-- a1 <- field a 1
-- -- Need to duplicate b because it's used twice
-- (b0, b1) <- makeDup bookRef b
-- r0 <- both bookRef a0 b0
-- r1 <- both bookRef a1 b1
-- makeNode _SUP_ (tLab a) [r0, r1]
-- we need to use the SAME label for the new dup, and the returned SUP.
-- reason about this and update the code to reflect this change verywhere it applies
-- (...)
-- I now believe that we should NOT use the freshLab field from the Book. in
-- fact, I removed it. we should still receive it so we can pass to reduce later
-- on. but, for now, let's instead use a global no-inline counter implemented
-- inside this file itself, for the fresh label generation.
-- final Check.hs:
module Check where
import Control.Monad (when)
import Data.IORef (IORef, readIORef, modifyIORef', newIORef)
import Data.Word
import System.IO.Unsafe (unsafePerformIO)
import Extract
import Foreign
import Reduce
import Type
-- Global counter for fresh labels
{-# NOINLINE freshLabelCounter #-}
freshLabelCounter :: IORef Lab
freshLabelCounter = unsafePerformIO $ newIORef 0xC00000
-- Constants
setT, empT, uniT, u32T, nilT, eraT :: Term
setT = termNew _SET_ 0 0
empT = termNew _EMP_ 0 0
uniT = termNew _UNI_ 0 0
u32T = termNew _U32_ 0 0
nilT = termNew _NIL_ 0 0
eraT = termNew _ERA_ 0 0
-- Runtime helpers
debug :: Term -> IO ()
debug term = do
core <- toCore term
putStr $ concatMap (\c -> if c == '\n' then "; " else [c]) $ show core
whnf :: Term -> IO Term
whnf = reduceC
field :: Term -> Int -> IO Term
field t i = do
got (tLoc t + fromIntegral i)
-- | Allocates a node and fills its children, returning the parent Term.
-- If the list of children is empty we return a leaf term (loc = 0).
makeNode :: Tag -> Lab -> [Term] -> IO Term
makeNode tag lab cs
| null cs = do
pure (termNew tag lab 0)
| otherwise = do
ptr <- allocNode (fromIntegral (length cs))
mapM_ (\ (i, c) -> set (ptr + fromIntegral i) c) (zip [0 ..] cs)
pure (termNew tag lab ptr)
-- | Create a DUP node that holds the original term, and return two DP0/DP1 terms
-- with the specified label
makeDup :: Lab -> Term -> IO (Term, Term)
makeDup lab term = do
-- Create a DUP node to hold the original term
loc <- allocNode 1
set loc term
-- Create the DP0/DP1 terms (leaf terms with the shared label)
let t0 = termNew _DP0_ lab 0
let t1 = termNew _DP1_ lab 0
-- Return the DP0/DP1 terms
pure (t0, t1)
-- | Gets a fresh label and increments the counter
genFreshLabel :: IO Lab
genFreshLabel = do
lab <- readIORef freshLabelCounter
modifyIORef' freshLabelCounter (+1)
pure lab
-- | Create a DUP node with a fresh label
makeDupFresh :: Term -> IO (Term, Term)
makeDupFresh term = do
-- Get a fresh label
freshLab <- genFreshLabel
makeDup freshLab term
-- | Boolean 'and', but for One|Era.
both :: Term -> Term -> IO Term
both a b
| tTag a == _SUP_ = do
a0 <- field a 0
a1 <- field a 1
-- Use the SUP's label when duplicating b
(b0, b1) <- makeDup (tLab a) b
r0 <- both a0 b0
r1 <- both a1 b1
makeNode _SUP_ (tLab a) [r0, r1]
| tTag b == _SUP_ = do
b0 <- field b 0
b1 <- field b 1
-- Use the SUP's label when duplicating a
(a0, a1) <- makeDup (tLab b) a
r0 <- both a0 b0
r1 <- both a1 b1
makeNode _SUP_ (tLab b) [r0, r1]
| tTag a == _NIL_ && tTag b == _NIL_ = do
pure nilT
| otherwise = do
pure eraT
-- Structural equality - returns nilT for equal, eraT for not equal
equal :: Int -> Term -> Term -> IO Term
equal dep a0 b0 = do
a <- whnf a0
b <- whnf b0
putStrLn $ "== " ++ showTerm a
putStrLn $ ".. " ++ showTerm b
if a == b
then pure nilT
else equalGo dep a b
equalGo :: Int -> Term -> Term -> IO Term
equalGo dep a b
| tTag a == _SUP_ = do
a0 <- field a 0
a1 <- field a 1
(b0, b1) <- makeDup (tLab a) b
r0 <- equalGo dep a0 b0
r1 <- equalGo dep a1 b1
makeNode _SUP_ (tLab a) [r0, r1]
| tTag b == _SUP_ = do
b0 <- field b 0
b1 <- field b 1
(a0, a1) <- makeDup (tLab b) a
r0 <- equalGo dep a0 b0
r1 <- equalGo dep a1 b1
makeNode _SUP_ (tLab b) [r0, r1]
| tTag a == _ERA_ && tTag b == _ERA_ = do
pure nilT
| tTag a == _SET_ && tTag b == _SET_ = do
pure nilT
| tTag a == _EMP_ && tTag b == _EMP_ = do
pure nilT
| tTag a == _UNI_ && tTag b == _UNI_ = do
pure nilT
| tTag a == _NIL_ && tTag b == _NIL_ = do
pure nilT
| tTag a == _U32_ && tTag b == _U32_ = do
pure nilT
| tTag a == _W32_ && tTag b == _W32_ = do
if tLab a == tLab b
then pure nilT
else pure eraT
| tTag a == _SIG_ && tTag b == _SIG_ = do
ax <- field a 0
ay <- field a 1
bx <- field b 0
by <- field b 1
rx <- equalGo dep ax bx
ry <- equalGo dep ay by
both rx ry
| tTag a == _TUP_ && tTag b == _TUP_ = do
ax <- field a 0
ay <- field a 1
bx <- field b 0
by <- field b 1
rx <- equalGo dep ax bx
ry <- equalGo dep ay by
both rx ry
| tTag a == _ALL_ && tTag b == _ALL_ = do
aa <- field a 0
ab <- field a 1
ba <- field b 0
bb <- field b 1
ra <- equalGo dep aa ba
rb <- equalGo dep ab bb
both ra rb
| tTag a == _LAM_ && tTag b == _LAM_ = do
var0 <- makeNode _W32_ (fromIntegral dep) []
var1 <- makeNode _W32_ (fromIntegral dep) []
aApp <- makeNode _APP_ 0 [a, var0]
bApp <- makeNode _APP_ 0 [b, var1]
aRes <- whnf aApp
bRes <- whnf bApp
equalGo (dep+1) aRes bRes
| tTag a == _APP_ && tTag b == _APP_ = do
af <- field a 0
ax <- field a 1
bf <- field b 0
bx <- field b 1
rf <- equalGo dep af bf
rx <- equalGo dep ax bx
both rf rx
| tTag a == _ANN_ && tTag b == _ANN_ = do
av <- field a 0
at <- field a 1
bv <- field b 0
bt <- field b 1
rv <- equalGo dep av bv
rt <- equalGo dep at bt
both rv rt
| otherwise = do
pure eraT
-- Inference
infer :: Int -> Term -> IO Term
infer dep term = do
-- putStr "infer: " ; debug term ; putStrLn ""
putStrLn $ "infer: " ++ showTerm term
inferGo dep term
inferGo :: Int -> Term -> IO Term
inferGo dep term
| tTag term == _SUP_ = do
a <- field term 0
b <- field term 1
aType <- infer dep a
bType <- infer dep b
makeNode _SUP_ (tLab term) [aType, bType]
| tTag term == _VAR_ = do
sub <- got (termLoc term)
if termGetBit sub == 0
then do
putStr "(cant-infer-var)"
pure eraT
else do
infer dep (termRemBit sub)
| tTag term == _REF_ = do
putStr "(cant-infer-ref)"
pure eraT
| tTag term == _LET_ = do
val <- field term 0
bod <- field term 1
valType <- infer dep val
if tTag valType == _ERA_
then pure eraT
else infer (dep+1) bod
| tTag term == _ERA_ = do
pure setT
| tTag term == _SET_ = do
pure setT
| tTag term == _EMP_ = do
pure setT
| tTag term == _EFQ_ = do
x <- field term 0
rc <- check dep x empT
if tTag rc == _ERA_
then pure eraT
else do
lamB <- makeNode _LAM_ 0 [empT]
makeNode _ALL_ 0 [empT, lamB]
| tTag term == _UNI_ = do
pure setT
| tTag term == _NIL_ = do
pure uniT
| tTag term == _USE_ = do
putStr "(cant-infer-use)"
pure eraT
| tTag term == _U32_ = do
pure setT
| tTag term == _W32_ = do
pure u32T
| tTag term == _SWI_ = do
putStr "(cant-infer-swi)"
pure eraT
| tTag term == _OPX_ || tTag term == _OPY_ = do
putStr "(cant-infer-op)"
pure eraT
| tTag term == _SIG_ = do
a <- field term 0
b <- field term 1
(a0, a1) <- makeDupFresh a
aOk <- check dep a0 setT
if tTag aOk == _ERA_
then pure eraT
else do
lamT <- makeNode _LAM_ 0 [setT]
allT <- makeNode _ALL_ 0 [a1, lamT]
bOk <- check dep b allT
if tTag bOk == _ERA_
then pure eraT
else pure setT
| tTag term == _TUP_ = do
a <- field term 0
b <- field term 1
aType <- infer dep a
if tTag aType == _ERA_
then pure eraT
else do
bType <- infer dep b
if tTag bType == _ERA_
then pure eraT
else do
lamB <- makeNode _LAM_ 0 [bType]
makeNode _SIG_ 0 [aType, lamB]
| tTag term == _GET_ = do
putStr "(cant-infer-get)"
pure eraT
| tTag term == _ALL_ = do
a <- field term 0
b <- field term 1
aOk <- check dep a setT
if tTag aOk == _ERA_
then pure eraT
else do
lamT <- makeNode _LAM_ 0 [setT]
allT <- makeNode _ALL_ 0 [setT, lamT]
bOk <- check dep b allT
if tTag bOk == _ERA_
then pure eraT
else pure setT
| tTag term == _LAM_ = do
putStr "(cant-infer-lam)"
pure eraT
| tTag term == _APP_ = do
f <- field term 0
x <- field term 1
(x0, x1) <- makeDupFresh x
fType <- infer dep f
if tTag fType == _ERA_
then pure eraT
else do
fTypeNf <- whnf fType
if tTag fTypeNf /= _ALL_
then do
putStr "(non-fun-app)"
pure eraT
else do
a <- field fTypeNf 0
b <- field fTypeNf 1
xOk <- check dep x0 a
if tTag xOk == _ERA_
then pure eraT
else do
app <- makeNode _APP_ 0 [b, x1]
whnf app
| tTag term == _ANN_ = do
if termLab term == 0
then do -- already checked
tm <- field term 0
ty <- field term 1
pure ty
else do
tm <- field term 0
ty <- field term 1
rc <- check dep tm ty
if tTag rc == _ERA_
then pure eraT
else pure ty
| otherwise = do
putStr $ "(cant-infer-unknown)"
pure eraT
-- Checking
check :: Int -> Term -> Term -> IO Term
check dep term goal0 = do
goal <- whnf goal0
-- putStr "check: " ; debug term ; putStr " :: " ; debug goal ; putStrLn ""
putStrLn $ "check: " ++ showTerm term ++ " :: " ++ showTerm goal
checkGo dep term goal
checkGo :: Int -> Term -> Term -> IO Term
checkGo dep term goal
| tTag term == _SUP_ = do
a <- field term 0
b <- field term 1
-- Use the SUP's label when duplicating goal
(goal0, goal1) <- makeDup (tLab term) goal
ra <- checkGo dep a goal0
rb <- checkGo dep b goal1
makeNode _SUP_ (tLab term) [ra, rb]
| tTag goal == _SUP_ = do
a <- field goal 0
b <- field goal 1
-- Use the SUP's label when duplicating term
(term0, term1) <- makeDup (tLab goal) term
ra <- checkGo dep term0 a
rb <- checkGo dep term1 b
makeNode _SUP_ (tLab goal) [ra, rb]
| tTag term == _ERA_ = do
pure nilT
| tTag term == _VAR_ = do
sub <- got (termLoc term)
if termGetBit sub == 0
then do
putStr "(cant-infer-var)"
pure eraT
else do
check dep (termRemBit sub) goal
| tTag term == _NIL_ && tTag goal == _UNI_ = do
pure nilT
| tTag term == _W32_ && tTag goal == _U32_ = do
pure nilT
| tTag term == _SET_ && tTag goal == _SET_ = do
pure nilT
| tTag term == _EMP_ && tTag goal == _SET_ = do
pure nilT
| tTag term == _UNI_ && tTag goal == _SET_ = do
pure nilT
| tTag term == _U32_ && tTag goal == _SET_ = do
pure nilT
| tTag term == _SIG_ && tTag goal == _SET_ = do
a <- field term 0
b <- field term 1
-- Duplicate 'a' before first use
(a0, a1) <- makeDupFresh a
aOk <- check dep a0 setT
if tTag aOk == _ERA_
then pure eraT
else do
lamT <- makeNode _LAM_ 0 [setT]
allT <- makeNode _ALL_ 0 [a1, lamT]
bOk <- check dep b allT
if tTag bOk == _ERA_
then pure eraT
else pure nilT
| tTag term == _ALL_ && tTag goal == _SET_ = do
a <- field term 0
b <- field term 1
aOk <- check dep a setT
if tTag aOk == _ERA_
then do
pure eraT
else do
lamT <- makeNode _LAM_ 0 [setT]
allT <- makeNode _ALL_ 0 [setT, lamT]
bOk <- check dep b allT
if tTag bOk == _ERA_
then pure eraT
else pure nilT
| tTag term == _LAM_ && tTag goal == _ALL_ = do
a <- field goal 0
b <- field goal 1
(a0, a1) <- makeDupFresh a
-- Create fresh variables for the lambda binding
-- NOTE: temporarily, we will use W32 to simulate bruijn indices
-- this causes nums to identify with vars, but is otherwise fine
let var0 = termNew _W32_ 0 (fromIntegral dep)
let var1 = termNew _W32_ 0 (fromIntegral dep)
-- Annotate variables with types
ann0 <- makeNode _ANN_ 0 [var0, a0]
ann1 <- makeNode _ANN_ 0 [var1, a1]
-- Apply function body and type body to variable
appF <- makeNode _APP_ 0 [term, ann0]
appB <- makeNode _APP_ 0 [b, ann1]
appF_nf <- reduceAppLam appF term
-- appF_nf <- return appF
appB_nf <- whnf appB
-- print $ showTerm b
-- print $ showTerm appB
-- print $ showTerm appB_nf
-- print $ showTerm appF
-- Check with increased depth
check (dep + 1) appF_nf appB_nf
| tTag term == _TUP_ && tTag goal == _SIG_ = do
a <- field term 0
b <- field term 1
aTy <- field goal 0
bTy <- field goal 1
-- Duplicate 'a' before first use
(a0, a1) <- makeDupFresh a
-- Check first component
aOk <- check dep a0 aTy
if tTag aOk == _ERA_
then pure eraT
else do
-- Apply dependent type to first component
app <- makeNode _APP_ 0 [bTy, a1]
bTyA <- whnf app
-- Check second component
bOk <- check dep b bTyA
if tTag bOk == _ERA_
then pure eraT
else pure nilT
| tTag term == _EFQ_ = do
c <- field term 0
rc <- check dep c empT
if tTag rc == _ERA_
then pure eraT
else pure nilT
| tTag term == _USE_ = do
c <- field term 0
f <- field term 1
rc <- check dep c uniT
if tTag rc == _ERA_
then pure eraT
else do
rf <- check dep f goal
if tTag rf == _ERA_
then pure eraT
else pure nilT
| tTag term == _GET_ = do
c <- field term 0
f <- field term 1
cType <- infer dep c
if tTag cType == _ERA_
then pure eraT
else do
cTypeNf <- whnf cType
if tTag cTypeNf /= _SIG_
then do
putStr "(expected-pair-type)"
pure eraT
else do
rf <- check dep f goal
if tTag rf == _ERA_
then pure eraT
else pure nilT
| tTag term == _SWI_ = do
c <- field term 0
z <- field term 1
s <- field term 2
rc <- check dep c u32T
if tTag rc == _ERA_
then pure eraT
else do
-- Get a fresh label for duplicating the goal
freshLab <- genFreshLabel
(goal0, goal1) <- makeDup freshLab goal
rz <- check dep z goal0
if tTag rz == _ERA_
then pure eraT
else do
rs <- check dep s goal1
if tTag rs == _ERA_
then pure eraT
else pure nilT
| otherwise = do
ty <- infer dep term
if tTag ty == _ERA_
then pure eraT
else do
eq <- equal dep ty goal
if tTag eq == _NIL_
then pure nilT
else do
putStr "(type-mismatch)"
pure eraT
-- PROBLEM: I'm attempting to type-check the following HVM3 file:
-- @main = <λx.x : ∀x:U32.U32>
-- yet, when running the `hvm` command on that file, I get:
-- <λa.a : ∀U32.λb.U32>
-- Checking @main...
-- check: term_new(LAM,0x000000,0x00000009) :: term_new(ALL,0x000000,0x0000000a)
-- check: term_new(VAR,0x000000,0x00000009) :: term_new(U32,0x000000,0x00000000)
-- check: term_new(ANN,0x000000,0x0000000e) :: term_new(U32,0x000000,0x00000000)
-- infer: term_new(ANN,0x000000,0x0000000e)
-- == term_new(LAM,0x000000,0x00000016)
-- .. term_new(U32,0x000000,0x00000000)
-- (type-mismatch)✗
-- WORK: 5 interactions
-- TIME: 0.0004910 seconds
-- SIZE: 28 nodes
-- this is unexpected. instead, I expected this term to type-check fine.
-- in particular, that equality should be U32 == U32, not LAM == U32.
-- that means there is some error in my code.
-- reason about the issue, find the exact location of the error, and
-- propose the simplest change that will fix this specific issue.
@VictorTaelin
Copy link
Author

Answer

the issue is on the makeDup function:

-- | Create a DUP node that holds the original term, and return two DP0/DP1 terms
--   with the specified label
makeDup :: Lab -> Term -> IO (Term, Term)
makeDup lab term = do
  -- Create a DUP node to hold the original term
  loc <- allocNode 1
  set loc term
  -- Create the DP0/DP1 terms (leaf terms with the shared label)
  let t0 = termNew _DP0_ lab 0
  let t1 = termNew _DP1_ lab 0
  -- Return the DP0/DP1 terms
  pure (t0, t1)

it is allocating a location for the DUP node, but it is returning DP0/DP1 links
pointing to 0, instead of that location. this causes incorrect DUP interactions,
which, in turn, make that lambda show up there.

the solution is to replace t0 and t1 by:

  let t0 = termNew _DP0_ lab loc
  let t1 = termNew _DP1_ lab loc

note that, in isolation, spotting that this function is wrong is not hard, but realizing that it is the underlying cause for the issue that I reported absolutely is. if an AI hyper-focuses on the issue at hands, it will only succeeds if it has extraordinary debugging skills

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment