packages feed

free-foil-0.4.0: src/Control/Monad/Free/Foil/Binary.hs

{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE LambdaCase            #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes            #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE UndecidableInstances  #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- | 'Binary' instances for the scope-safe syntax: the wire view of a term
-- is the term itself, raw ids and all.
--
-- The instances are deliberately orphans in a module of their own, so that
-- they are opt-in: importing this module is what brings them into scope,
-- and nothing else in the library does. (The dependency this costs is
-- @binary@, a GHC boot library.)
--
-- Note that decoding /mints/ scope evidence: a 'Foil.Name' comes back at
-- whatever scope index the context asks for, and the existential scope
-- under a binder is chosen arbitrarily. Thus the instances are a trust
-- boundary, in the sense of 'Control.Monad.Foil.Blocks.checkExtScope'. The
-- bytes are meaningful only under the discipline of the layer that wrote
-- them, and that layer is expected to validate what it can on the way in.
-- In particular, it should resolve the references it made
-- world-independent, and check that the names it left verbatim lie where its
-- allocation policy says. "Control.Monad.Free.Foil.Artifact" supplies those
-- checks.
module Control.Monad.Free.Foil.Binary () where

import           Data.Binary                 (Binary (..))
import           Data.Binary.Get             (Get, getWord8)
import           Data.Binary.Put             (putWord8)

import           Control.Monad.Foil.Internal
import           Control.Monad.Free.Foil     (AST (..), ScopedAST (..))

-- | The raw id and nothing else. See the module documentation for what
-- decoding trusts.
instance Binary (Name n) where
  put (UnsafeName raw) = put raw
  get = UnsafeName <$> get

-- | See the 'Binary' instance of 'Name'.
instance Binary (NameBinder n l) where
  put (UnsafeNameBinder name) = put name
  get = UnsafeNameBinder <$> get

-- | The two bounds. A range carries no scope index, so nothing is minted:
-- this instance is layout metadata for the serialising layer.
instance Binary NameRange where
  put (NameRange lo hi) = put lo <> put hi
  get = NameRange <$> get <*> get

-- | The binder and the body, one after the other. Decoding mints the scope
-- under the binder. See the module documentation.
instance (forall x y. Binary (binder x y), forall l. Binary (AST binder sig l))
    => Binary (ScopedAST binder sig n) where
  put (ScopedAST binder body) = put binder <> put body
  get = do
    binder <- get :: Get (binder n n)
    body <- get
    pure (ScopedAST binder body)

-- | A tag byte, then the name or the node.
instance ( forall x y. Binary (binder x y)
         , forall scope term. (Binary scope, Binary term) => Binary (sig scope term)
         ) => Binary (AST binder sig n) where
  put (Var x)     = putWord8 0 <> put x
  put (Node node) = putWord8 1 <> put node
  get = getWord8 >>= \case
    0   -> Var <$> get
    1   -> Node <$> get
    tag -> fail ("unknown AST tag " <> show tag)