-- In this case, a^n corresponds to an n-tuple whose elements are of type a
-- We have a typeclass

class Conv a b where conv :: a -> b

-- And types corresponding to

type Tup t = forall n : Nat. t ^ n
type Var t = forall n : Nat. (forall s. Conv s t => s) ^ n

-- we also have these two built-in primitives

generalize :: Var t -> Tup t
forget :: Tup t -> [t]

-- that will usually be used together

var :: Var t -> [t]

-- We could use this to create 'variadic' functions, e.g.
-- a non-type-safe but C-like printf

data PFElem = PFInt Int | PFFloat Float | PFString String

instance Conv Int PFElem where conv = PFInt
instance Conv Float PFElem where conv = PFFloat
instance Conv String PFElem where conv = PFString

printf :: String -> Var PFElem -> IO ()
printf spec args = putStr . printfInterpolate . var $ args
  where printfInterpolate :: String -> [PFElem] -> String
        printfInterpolate = {- ... -}

main :: IO ()
main = printf "string %s and int %d\n" ("foo", 5)

--------------------------------------------------------------------------------

-- In Latka, we will have the `Var` type and the `var` primitive as built-ins,
-- and treats a^n as a subtype of Var a in all cases. Thus, we can convert a
-- value like <2,3> : Nat * Nat to a value of type Var Nat, but not the other
-- way around.

-- With that machinery, we can define the following as a library:

-- word
instance Conv String WordPart where {- ... -}
instance Conv Word   WordPart where {- ... -}

wd : Var WordPart -> Word
wd = {- ... -}

-- this is well-typed, because its argument is of type String * Word <: Var WordPart
puts wd.<"foo", wd.<"bar", "baz">>
-- this is not, because its argument is of type String * String * (Nat * Nat)
-- puts wd.<"a","b",<1,2>>


-- sentence
instance Conv String   SentencePart where {- ... -}
instance Conv Word     SentencePart where {- ... -}
instance Conv Sentence SentencePart where {- ... -}

se : Var SentencePart -> Sentence
se = {- ... -}

-- this is well-typed, as the argument is of type
-- String * Word * Sentence <: Var SentencePart
puts se.<"one", wd.<"two", "three">, se.<"four", "five">>

-- &c

--this gives us type-safe variadic functions, as well as a nice mechanism for
--semi-implicitly stringifying things

cat : Var String -> String
cat vs = go.(var.vs)
  where go []      = ""
        go (x::xs) = x (go.xs)

{- prints "foo5<>" -}
puts cat.<"foo", 5, <>>
