123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- ..
- ::
- module language.foreign-function-interface where
- open import Agda.Primitive
- open import Agda.Builtin.Bool
- postulate <Name> : Set
- .. _foreign-function-interface:
- **************************
- Foreign Function Interface
- **************************
- .. contents::
- :depth: 2
- :local:
- Compiler Pragmas
- ================
- There are two backend-generic pragmas used for the FFI::
- {-# COMPILE <Backend> <Name> <Text> #-}
- {-# FOREIGN <Backend> <Text> #-}
- The ``COMPILE`` pragma associates some information ``<Text>`` with a
- name ``<Name>`` defined in the same module, and the ``FOREIGN`` pragma
- associates ``<Text>`` with the current top-level module. This
- information is interpreted by the specific backend during compilation
- (see below). These pragmas were added in Agda 2.5.3.
- Haskell FFI
- ===========
- .. note::
- This section applies to the :ref:`ghc-backend`.
- The ``FOREIGN`` pragma
- ----------------------
- The GHC backend interprets ``FOREIGN`` pragmas as inline Haskell code and can
- contain arbitrary code (including import statements) that will be added to the
- compiled module. For instance::
- {-# FOREIGN GHC import Data.Maybe #-}
- {-# FOREIGN GHC
- data Foo = Foo | Bar Foo
- countBars :: Foo -> Integer
- countBars Foo = 0
- countBars (Bar f) = 1 + countBars f
- #-}
- The ``COMPILE`` pragma
- ----------------------
- There are four forms of ``COMPILE`` annotations recognized by the GHC backend
- .. code-block:: agda
- {-# COMPILE GHC <Name> = <HaskellCode> #-}
- {-# COMPILE GHC <Name> = type <HaskellType> #-}
- {-# COMPILE GHC <Name> = data <HaskellData> (<HsCon1> | .. | <HsConN>) #-}
- {-# COMPILE GHC <Name> as <HaskellName> #-}
- The first three tells the compiler how to compile a given Agda definition and the last
- exposes an Agda definition under a particular Haskell name allowing Agda libraries to
- be used from Haskell.
- .. _compiled_type_pragma:
- Using Haskell Types from Agda
- -----------------------------
- In order to use a Haskell function from Agda its type must be mapped to an Agda
- type. This mapping can be configured using the ``type`` and ``data`` forms of the
- ``COMPILE`` pragma.
- Opaque types
- ^^^^^^^^^^^^
- Opaque Haskell types are exposed to Agda by postulating an Agda type and
- associating it to the Haskell type using the ``type`` form of the ``COMPILE``
- pragma::
- {-# FOREIGN GHC import qualified System.IO #-}
- postulate FileHandle : Set
- {-# COMPILE GHC FileHandle = type System.IO.Handle #-}
- This tells the compiler that the Agda type ``FileHandle`` corresponds to the Haskell
- type ``System.IO.Handle`` and will enable functions using file handles to be used
- from Agda.
- Data types
- ^^^^^^^^^^
- Non-opaque Haskell data types can be mapped to Agda datatypes using the ``data`` form
- of the ``COMPILED`` pragma::
- data Maybe (A : Set) : Set where
- nothing : Maybe A
- just : A → Maybe A
- {-# COMPILE GHC Maybe = data Maybe (Nothing | Just) #-}
- The compiler checks that the types of the Agda constructors match the types of the
- corresponding Haskell constructors and that no constructors have been left out
- (on either side).
- Built-in Types
- ^^^^^^^^^^^^^^
- The GHC backend compiles certain Agda :ref:`built-in types <built-ins>` to
- special Haskell types. The mapping between Agda built-in types and Haskell
- types is as follows:
- ============= ==================
- Agda Built-in Haskell Type
- ============= ==================
- ``NAT`` ``Integer``
- ``INTEGER`` ``Integer``
- ``STRING`` ``Data.Text.Text``
- ``CHAR`` ``Char``
- ``BOOL`` ``Bool``
- ``FLOAT`` ``Double``
- ============= ==================
- .. warning::
- Haskell code manipulating Agda natural numbers as integers must take
- care to avoid negative values.
- .. warning::
- Agda ``FLOAT`` values have only one logical ``NaN`` value. At runtime,
- there might be multiple different ``NaN`` representations present. All
- such ``NaN`` values must be treated equal by FFI calls.
- .. _compiled_pragma:
- Using Haskell functions from Agda
- ---------------------------------
- Once a suitable mapping between Haskell types and Agda types has been set
- up, Haskell functions whose types map to Agda types can be exposed to Agda
- code with a ``COMPILE`` pragma::
- open import Agda.Builtin.IO
- open import Agda.Builtin.String
- open import Agda.Builtin.Unit
- {-# FOREIGN GHC
- import qualified Data.Text.IO as Text
- import qualified System.IO as IO
- #-}
- postulate
- stdout : FileHandle
- hPutStrLn : FileHandle → String → IO ⊤
- {-# COMPILE GHC stdout = IO.stdout #-}
- {-# COMPILE GHC hPutStrLn = Text.hPutStrLn #-}
- The compiler checks that the type of the given Haskell code matches the
- type of the Agda function. Note that the ``COMPILE`` pragma only affects
- the runtime behaviour--at type-checking time the functions are treated as
- postulates.
- .. warning::
- It is possible to give Haskell definitions to defined (non-postulate)
- Agda functions. In this case the Agda definition will be used at
- type-checking time and the Haskell definition at runtime. However, there
- are no checks to ensure that the Agda code and the Haskell code behave
- the same and **discrepancies may lead to undefined behaviour**.
- This feature can be used to let you reason about code involving calls to
- Haskell functions under the assumption that you have a correct Agda model
- of the behaviour of the Haskell code.
- Using Agda functions from Haskell
- ---------------------------------
- Since Agda 2.3.4 Agda functions can be exposed to Haskell code using
- the ``as`` form of the ``COMPILE`` pragma::
- module IdAgda where
- idAgda : ∀ {A : Set} → A → A
- idAgda x = x
- {-# COMPILE GHC idAgda as idAgdaFromHs #-}
- This tells the compiler that the Agda function ``idAgda`` should be compiled
- to a Haskell function called ``idAgdaFromHs``. Without this pragma, functions
- are compiled to Haskell functions with unpredictable names and, as a result,
- cannot be invoked from Haskell. The type of ``idAgdaFromHs`` will be the translated
- type of ``idAgda``.
- The compiled and exported function ``idAgdaFromHs`` can then be imported and
- invoked from Haskell like this:
- .. code-block:: haskell
- -- file UseIdAgda.hs
- module UseIdAgda where
- import MAlonzo.Code.IdAgda (idAgdaFromHs)
- -- idAgdaFromHs :: () -> a -> a
- idAgdaApplied :: a -> a
- idAgdaApplied = idAgdaFromHs ()
- Polymorphic functions
- ---------------------
- Agda is a monomorphic language, so polymorphic functions are modeled
- as functions taking types as arguments. These arguments will be
- present in the compiled code as well, so when calling polymorphic
- Haskell functions they have to be discarded explicitly. For instance,
- ::
- postulate
- ioReturn : {A : Set} → A → IO A
- {-# COMPILE GHC ioReturn = \ _ x -> return x #-}
- In this case compiled calls to ``ioReturn`` will still have ``A`` as an
- argument, so the compiled definition ignores its first argument
- and then calls the polymorphic Haskell ``return`` function.
- Level-polymorphic types
- -----------------------
- :ref:`Level-polymorphic types <universe-levels>` face a similar problem to
- polymorphic functions. Since Haskell does not have universe levels the Agda
- type will have more arguments than the corresponding Haskell type. This can be solved
- by defining a Haskell type synonym with the appropriate number of phantom
- arguments. For instance:
- ::
- data Either {a b} (A : Set a) (B : Set b) : Set (a ⊔ b) where
- left : A → Either A B
- right : B → Either A B
- {-# FOREIGN GHC type AgdaEither a b = Either #-}
- {-# COMPILE GHC Either = data AgdaEither (Left | Right) #-}
- Handling typeclass constraints
- ------------------------------
- There is (currently) no way to map a Haskell type with type class constraints to an
- Agda type. This means that functions with class constraints cannot be used from Agda.
- However, this can be worked around by wrapping class constraints in Haskell data types,
- and providing Haskell functions using explicit dictionary passing.
- For instance, suppose we have a simple GUI library in Haskell:
- .. code-block:: haskell
- module GUILib where
- class Widget w
- setVisible :: Widget w => w -> Bool -> IO ()
- data Window
- instance Widget Window
- newWindow :: IO Window
- To use this library from Agda we first define a Haskell type for widget dictionaries and map this
- to an Agda type ``Widget``::
- {-# FOREIGN GHC import GUILib #-}
- {-# FOREIGN GHC data WidgetDict w = Widget w => WidgetDict #-}
- postulate
- Widget : Set → Set
- {-# COMPILE GHC Widget = type WidgetDict #-}
- We can then expose ``setVisible`` as an Agda function taking a Widget
- :ref:`instance argument <instance-arguments>`::
- postulate
- setVisible : {w : Set} {{_ : Widget w}} → w → Bool → IO ⊤
- {-# COMPILE GHC setVisible = \ _ WidgetDict -> setVisible #-}
- Note that the Agda ``Widget`` argument corresponds to a ``WidgetDict`` argument
- on the Haskell side. When we match on the ``WidgetDict`` constructor in the Haskell
- code, the packed up dictionary will become available for the call to ``setVisible``.
- The window type and functions are mapped as expected and we also add an Agda instance
- packing up the ``Widget Window`` Haskell instance into a ``WidgetDict``::
- postulate
- Window : Set
- newWindow : IO Window
- instance WidgetWindow : Widget Window
- {-# COMPILE GHC Window = type Window #-}
- {-# COMPILE GHC newWindow = newWindow #-}
- {-# COMPILE GHC WidgetWindow = WidgetDict #-}
- ..
- ::
- infixr 1 _>>=_
- postulate
- return : {A : Set} → A → IO A
- _>>=_ : {A B : Set} → IO A → (A → IO B) → IO B
- {-# COMPILE GHC return = \ _ -> return #-}
- {-# COMPILE GHC _>>=_ = \ _ _ -> (>>=) #-}
- We can then write code like this::
- openWindow : IO Window
- openWindow = newWindow >>= λ w →
- setVisible w true >>= λ _ →
- return w
- JavaScript FFI
- ==============
- The :ref:`JavaScript backend <javascript-backend>` recognizes ``COMPILE`` pragmas of the following form::
- {-# COMPILE JS <Name> = <JsCode> #-}
- where ``<Name>`` is a postulate, constructor, or data type. The code for a data type is used to compile
- pattern matching and should be a function taking a value of the data type and a table of functions
- (corresponding to case branches) indexed by the constructor names. For instance, this is the compiled
- code for the ``List`` type, compiling lists to JavaScript arrays::
- data List {a} (A : Set a) : Set a where
- [] : List A
- _∷_ : (x : A) (xs : List A) → List A
- {-# COMPILE JS List = function(x,v) {
- if (x.length < 1) {
- return v["[]"]();
- } else {
- return v["_∷_"](x[0], x.slice(1));
- }
- } #-}
- {-# COMPILE JS [] = Array() #-}
- {-# COMPILE JS _∷_ = function (x) { return function(y) { return Array(x).concat(y); }; } #-}
|