fir-0.1.0.0: src/FIR/Syntax/Option.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-|
Module: FIR.Syntax.Option
Custom 'Option' type (like 'Maybe') and associated functionality.
-}
module FIR.Syntax.Option
( Option(..)
, some, none
, option, fromOption
)
where
-- base
import Prelude hiding ((&&))
import Control.Monad
( ap )
-- fir
import Control.Type.Optic
( Name, ReifiedGetter(view) )
import Data.Type.Map
( (:->)((:->)) )
import FIR.AST
( Code
, Syntactic(..)
, SyntacticVal, InternalType
, pattern Lit, pattern Undefined, pattern Struct
)
import FIR.AST.Type
( AugType(Val) )
import FIR.Prim.Struct
( Struct(..) )
import FIR.Prim.Types
( PrimTy )
import FIR.Syntax.AST
( )
import FIR.Syntax.IfThenElse
( )
import FIR.Syntax.Program
( )
import Math.Logic.Class
( Choose(..), Boolean((&&)) )
--------------------------------------------------------------------------------------
-- | 'Option' type like in Axelsson–Svenningsson.
data Option a = Option { isSome :: Code Bool, fromSome :: a }
deriving stock Functor
instance Applicative Option where
pure = some
(<*>) = ap
instance Monad Option where
return = pure
Option { isSome = c, fromSome = a } >>= f
| Option { isSome = d, fromSome = b } <- f a
= Option { isSome = c && d, fromSome = b }
instance ( SyntacticVal a, PrimTy (InternalType a) ) => Syntactic (Option a) where
type Internal (Option a) =
Val (Struct '[ "isSome" ':-> Bool, "fromSome" ':-> InternalType a ])
toAST (Option { isSome = c, fromSome = a })
= choose c
( Struct ( Lit True :& toAST a :& End ) )
( Struct ( Lit False :& Undefined :& End ) )
fromAST struct
= Option
{ isSome = view @(Name "isSome" ) struct
, fromSome = fromAST (view @(Name "fromSome") struct)
}
some :: a -> Option a
some a = Option { isSome = Lit True, fromSome = a }
none :: forall a. ( SyntacticVal a, PrimTy (InternalType a) ) => Option a
none = Option { isSome = Lit False, fromSome = fromAST $ Undefined @(InternalType a) }
option
:: ( SyntacticVal a
, PrimTy (InternalType a)
, Choose (Code Bool) '(b,b,b)
)
=> b -> (a -> b) -> Option a -> b
option b f a_opt =
choose ( isSome a_opt ) ( f ( fromSome a_opt ) ) b
fromOption :: ( SyntacticVal a, PrimTy (InternalType a), Choose (Code Bool) '(a,a,a) )
=> a -> Option a -> a
fromOption a = option a id