[Agda] Re: Agda FFI bindings
Alan Jeffrey
ajeffrey at bell-labs.com
Tue Sep 28 23:40:56 CEST 2010
OK, here's my attempt to patch things. The goal is to provide FFI
bindings for level-polymorphic datatypes (and if we happen to do some
other dependencies while we're at it, then so much the better).
One slight wrinkle is that it's not obvious what the success criteria
are. I'm guessing the goal is that any program which successfully
compiles shouldn't produce any errors due to coercion, which can
probably be formalized using contract types, and blaming, together with
a result that says that if Agda is blamed, then there is a Haskell-free
program with the same runtime error, and dually if Haskell is blamed.
Brain-dump for a modification to the compiler follows. After that, an
implementation, which should be straightforward, ho ho ho.
The core of the existing translation is:
K[[ Set A ]] = *
K[[ x As ]] = undef
K[[ fn (x : A) B ]] = undef
K[[ Pi (x : A) B ]] = K[[ A ]] -> K[[ B ]]
K[[ k As ]] = undef
T[[ Set A ]] = undef
T[[ x As ]] = x T[[ As ]]
T[[ fn (x : A) B ]] = undef
T[[ Pi (x : A) B ]] =
if K[[ A ]] is defined
then forall x . Unit -> T[[ B ]]
else if x in fv B
then undef
else T[[ A ]] -> T[[ B ]]
T[[ k As ]] =
if COMPILED_TYPE k T
then T T[[ As ]]
else undef
E[[ Set A ]] = unit
E[[ x As ]] = x E[[ As ]]
E[[ fn (x : A) B ]] = fn x . E[[ B ]]
E[[ Pi (x : A) B ]] = unit
E[[ k As ]] =
if COMPILED k E
then E E[[ As ]]
else runtime-error
The change to get many uses of level-polymorphism to go through is:
K[[ k As ]] =
if COMPILED_TYPE k
then *
else undef
T[[ Set A ]] = Unit
T[[ Pi (x : A) B ]] =
if x in fv B
then forall x . T[[ A ]] -> T[[ B ]] -- Note: T[[A]] not Unit
else T[[ A ]] -> T[[ B ]]
Now, this isn't exactly backwards compatible, as there are some
translations which have changed, for example:
T[[ (Set -> Set) -> Set -> Set ]]
was Unit -> Unit -> Unit
now (Unit -> Unit) -> Unit-> Unit
However, this is actually a more accurate type, and indeed at runtime
expressions of this type will be fed functions, not unit values.
For example, on dependently-typed IO (assuming Level is bound to Int) we
get:
postulate
IO : {a : Level} -> (b : Set a) -> (Set a)
return : {a : Level} -> {b : Set a} -> b -> (IO b)
_>>=_ : {a b : Level} -> {c : Set a} -> {d : Set b} ->
(IO c) -> (c -> (IO d)) -> (IO d)
{-# COMPILED_TYPE IO AgdaIO #-}
{-# COMPILED return (\_ _ -> return) #-}
{-# COMPILED _>>=_ (\_ _ _ _ -> (>>=)) #-}
where type AgdaIO a b = IO b. This typechecks because:
AgdaIO
: * -> * -> *
= K [[ {a : Level} -> (b : Set a) -> (Set a) ]]
(\_ _ -> return)
: forall a -> Int -> forall b -> Unit -> b -> (AgdaIO a b)
= T [[ {a : Level} -> {b : Set a} -> b -> (IO {a} b) ]]
(\_ _ _ _ -> (>>=))
: forall a -> Int -> forall b -> Int ->
forall c -> Unit -> forall d -> Unit ->
(AgdaIO a c) -> (c -> (AgdaIO b d)) -> (AgdaIO b d)
= T [[ {a b : Level} -> {c : Set a} -> {d : Set b} ->
(IO {a} c) -> (c -> (IO {b} d)) -> (IO {b} d) ]]
Boy, that was a bit of a slog.
A.
On 09/24/2010 11:45 AM, Jeffrey, Alan S A (Alan) wrote:
> On 09/23/2010 11:43 PM, Nils Anders Danielsson wrote:
>> On 2010-09-20 14:34, Alan Jeffrey wrote:
>>> The source of these errors is HaskellTypes.hs -- it looks like there'd
>>> be an easy fix for universe-monomorphic bindings, which is to add a
>>> cluase to haskellType, something like:
>>>
>>> Lit (LitLevel _ _) -> "()"
>>
>> This seems wrong, because there is nothing which stops an Agda program
>> from pattern matching on a level. I hope that we will switch to
>> parametric universe-polymorphism soon, but this has not happened yet.
>
> OK, deep breath, my thoughts by me...
>
> There's (essentially) three translations from Agda to Haskell, one from
> kinds, one from well-kinded types, and one from well-typed expressions,
> e.g.:
>
> K[[ Set M ]] = *
> K[[ x A1 ... An ]] = undef
> K[[ fn (x : A) B ]] = undef
> K[[ Pi (x : A) B ]] = K[[ A ]] -> K[[ B ]]
>
> T[[ Set M ]] = undef
> T[[ x A1 ... An ]] = x T[[ A1 ]] ... T[[ An ]]
> T[[ fn (x : A) B ]] = undef
> T[[ Pi (x : A) B ]] =
> if K[[ A ]] is defined
> then forall x . () -> T[[ B ]]
> else if x in fn B
> then undef
> else T[[ A ]] -> T[[ B ]]
>
> E[[ Set M ]] = ()
> E[[ x A1 ... An ]] = x E[[ A1 ]] ... E[[ An ]]
> E[[ fn (x : A) B ]] = fn x . E[[ B ]]
> E[[ Pi (x : A) B ]] = ()
>
> The proposed treatment of levels is:
>
> K[[ Level ]] = *
> T[[ Level ]] = Int
> E[[ Level ]] = ()
>
> K[[ level n ]] = undef
> T[[ level n ]] = ()
> E[[ level n ]] = n
>
> Now, you're right there is a problem, because we can have functions that
> treat levels just as regular datatypes, e.g.
>
> data Foo : Level -> Set where
> foo : (a : Level) -> (Foo a)
>
> x : Foo zero
> x = foo zero
>
> breaks because the translation ends up with:
>
> foo : forall a . () -> (Foo a)
> x : Foo ()
> x = foo 0
>
> The translation should actually be:
>
> foo : forall a . Int -> Foo a
>
> I'll have to think about how to fix this, a first shot is:
>
> T[[ Pi (x : A) B ]] =
> if K[[ A ]] is defined
> then if T[[ A ]] is defined
> then forall x . T[[ A ]] -> T[[ B ]]
> else forall x . () -> T[[ B ]]
> else ... as before ...
>
> but this doesn't work, e.g. when A is itself a variable.
>
> A.
More information about the Agda
mailing list