inductive type
| bv  : Nat → type
| bit : type

open type

-- This is a "parameterized List" where `plist f types` contains
-- an element of Type `f tp` for each corresponding element `tp ∈ types`.
inductive plist (f : type → Type) : List type → Type
| nil {} : plist []
| cons {h:type} {r:List type} : f h → plist r → plist (h::r)

-- Operations on values; the first argument contains the types of
-- inputs, and the second for the return Type.
inductive op : List type → type → Type
| neq (tp:type) : op [tp, tp] bit
| mul (w : Nat) : op [bv w, bv w] (bv w)

-- Denotes expressions that evaluate to a number given a memory State and register to value map.
inductive value : type → Type
| const (w : Nat) : value (bv w)
| op {args:List type} {tp:type} : op args tp → plist value args → value tp

--- This creates a plist (borrowed from the List notation).

open value

-- This works
-- #eval value.op (op.mul 32) [[[ const 32, const 32 ]]]

-- This also works
-- instance bvHasMul (w : Nat) : HasMul (value (bv w)) := ⟨fun x y => w value.op (op.mul w) [[[x, y]]]⟩
-- #eval const 32 * const 32

-- This works
-- #eval value.op (op.neq (bv 32)) [[[ const 32, const 32 ]]]

-- def neq {tp:type} (x y : value tp) : value bit := value.op (op.neq tp) [[[x, y]]]
-- #eval neq (const 32) (const 32)
