packages feed

covenant-1.2.0: src/Covenant/Type.hs

{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}

-- |
-- Module: Covenant.Type
-- Copyright: (C) MLabs 2025
-- License: Apache 2.0
-- Maintainer: koz@mlabs.city, sean@mlabs.city
--
-- Covenant's type system and various ways to construct types.
--
-- @since 1.0.0
module Covenant.Type
  ( -- * Type abstractions
    AbstractTy (..),
    Renamed (..),

    -- * Computation types
    CompT (Comp0, Comp1, Comp2, Comp3, CompN),
    CompTBody (ReturnT, (:--:>), ArgsAndResult),
    arity,

    -- * Value types
    ValT (..),
    dataTypeT,
    dataType1T,
    dataType2T,
    BuiltinFlatT (..),
    byteStringT,
    integerT,
    stringT,
    tyvar,
    boolT,
    g1T,
    g2T,
    mlResultT,
    unitT,

    -- * Data declarations
    TyName (TyName),
    ConstructorName (ConstructorName),
    Constructor (Constructor),
    PlutusDataStrategy
      ( EnumData,
        ProductListData,
        ConstrData,
        NewtypeData
      ),
    DataEncoding (SOP, PlutusData, BuiltinStrategy),
    PlutusDataConstructor
      ( PlutusI,
        PlutusB,
        PlutusConstr,
        PlutusList,
        PlutusMap
      ),
    DataDeclaration (DataDeclaration, OpaqueData),
  )
where

import Control.Monad (guard)
import Covenant.DeBruijn (DeBruijn)
import Covenant.Index
  ( Count,
    Index,
    count0,
    count1,
    count2,
    count3,
    intCount,
  )
import Covenant.Internal.Strategy
  ( DataEncoding (BuiltinStrategy, PlutusData, SOP),
    PlutusDataConstructor
      ( PlutusB,
        PlutusConstr,
        PlutusI,
        PlutusList,
        PlutusMap
      ),
    PlutusDataStrategy
      ( ConstrData,
        EnumData,
        NewtypeData,
        ProductListData
      ),
  )
import Covenant.Internal.Type
  ( AbstractTy (BoundAt),
    BuiltinFlatT
      ( BLS12_381_G1_ElementT,
        BLS12_381_G2_ElementT,
        BLS12_381_MlResultT,
        BoolT,
        ByteStringT,
        IntegerT,
        StringT,
        UnitT
      ),
    CompT (CompT),
    CompTBody (CompTBody),
    Constructor (Constructor),
    ConstructorName (ConstructorName),
    DataDeclaration (DataDeclaration, OpaqueData),
    Renamed (Rigid, Unifiable, Wildcard),
    TyName (TyName),
    ValT (Abstraction, BuiltinFlat, Datatype, ThunkT),
    arity,
  )
import Data.Coerce (coerce)
import Data.Kind (Type)
import Data.Vector (Vector)
import Data.Vector qualified as Vector
import Data.Vector.NonEmpty (NonEmptyVector)
import Data.Vector.NonEmpty qualified as NonEmpty
import GHC.Exts (fromListN)
import Optics.Core (preview)

-- | The body of a computation type that doesn't take any arguments and produces
-- the a result of the given value type. Use this just as you would a
-- data constructor.
--
-- = Example
--
-- * @'ReturnT' 'integerT'@ is @!Integer@
--
-- @since 1.0.0
pattern ReturnT :: forall (a :: Type). ValT a -> CompTBody a
pattern ReturnT x <- CompTBody (returnHelper -> Just x)
  where
    ReturnT x = CompTBody (NonEmpty.singleton x)

-- | Given a type of argument, and the body of another computation type,
-- construct a copy of the body, adding an extra argument of the argument type.
-- Use this just as you would a data constructor.
--
-- = Note
--
-- Together with 'ReturnT', these two patterns provide an exhaustive pattern
-- match.
--
-- = Example
--
-- * @'integerT' :--:> ReturnT 'byteStringT'@ is @Integer -> !ByteString@
--
-- @since 1.0.0
pattern (:--:>) ::
  forall (a :: Type).
  ValT a ->
  CompTBody a ->
  CompTBody a
pattern x :--:> xs <- CompTBody (arrowHelper -> Just (x, xs))
  where
    x :--:> xs = CompTBody (NonEmpty.cons x (coerce xs))

infixr 1 :--:>

-- | A view of a computation type as a 'Vector' of its argument types, together
-- with its result type. Can be used as a data constructor, and is an exhaustive
-- match.
--
-- = Example
--
-- * @'ArgsAndResult' ('Vector.fromList' ['integerT', 'integerT']) 'integerT'@
--   is @Integer -> Integer -> !Integer@
--
-- @since 1.0.0
pattern ArgsAndResult ::
  forall (a :: Type).
  Vector (ValT a) ->
  ValT a ->
  CompTBody a
pattern ArgsAndResult args result <- (argsAndResultHelper -> (args, result))
  where
    ArgsAndResult args result = CompTBody (NonEmpty.snocV args result)

{-# COMPLETE ArgsAndResult #-}

{-# COMPLETE ReturnT, (:--:>) #-}

-- | A computation type that does not bind any type variables. Use this like a
-- data constructor.
--
-- @since 1.0.0
pattern Comp0 ::
  forall (a :: Type).
  CompTBody a ->
  CompT a
pattern Comp0 xs <- (countHelper 0 -> Just xs)
  where
    Comp0 xs = CompT count0 xs

-- | A computation type that binds one type variable (that
-- is, something whose type is @forall a . ... -> ...)@. Use this like a data
-- constructor.
--
-- @since 1.0.0
pattern Comp1 ::
  forall (a :: Type).
  CompTBody a ->
  CompT a
pattern Comp1 xs <- (countHelper 1 -> Just xs)
  where
    Comp1 xs = CompT count1 xs

-- | A computation type that binds two type variables (that
-- is, something whose type is @forall a b . ... -> ...)@. Use this like a data
-- constructor.
--
-- @since 1.0.0
pattern Comp2 ::
  forall (a :: Type).
  CompTBody a ->
  CompT a
pattern Comp2 xs <- (countHelper 2 -> Just xs)
  where
    Comp2 xs = CompT count2 xs

-- | A computation type that binds three type variables
-- (that is, something whose type is @forall a b c . ... -> ...)@. Use this like
-- a data constructor.
--
-- @since 1.0.0
pattern Comp3 ::
  forall (a :: Type).
  CompTBody a ->
  CompT a
pattern Comp3 xs <- (countHelper 3 -> Just xs)
  where
    Comp3 xs = CompT count3 xs

-- | A general way to construct and deconstruct computations which bind an
-- arbitrary number of type variables. Use this like a data constructor. Unlike
-- the other @Comp@ patterns, 'CompN' is exhaustive if matched on.
--
-- @since 1.0.0
pattern CompN ::
  Count "tyvar" ->
  CompTBody AbstractTy ->
  CompT AbstractTy
pattern CompN count xs <- CompT count xs
  where
    CompN count xs = CompT count xs

{-# COMPLETE CompN #-}

-- | Helper for defining type variables.
--
-- @since 1.0.0
tyvar :: DeBruijn -> Index "tyvar" -> ValT AbstractTy
tyvar db = Abstraction . BoundAt db

-- | Helper for referring to compound data types with no type variables.
--
-- @since 1.1.0
dataTypeT :: forall (a :: Type). TyName -> ValT a
dataTypeT tn = Datatype tn Vector.empty

-- | Helper for referring to compound data types with one type variable.
--
-- @since 1.1.0
dataType1T :: TyName -> ValT AbstractTy -> ValT AbstractTy
dataType1T tn = Datatype tn . Vector.singleton

-- | Helper for referring to compound data types with two type variables.
--
-- @since 1.1.0
dataType2T :: TyName -> ValT AbstractTy -> ValT AbstractTy -> ValT AbstractTy
dataType2T tn v1 v2 = Datatype tn . fromListN 2 $ [v1, v2]

-- | Helper for defining the value type of builtin bytestrings.
--
-- @since 1.0.0
byteStringT :: forall (a :: Type). ValT a
byteStringT = BuiltinFlat ByteStringT

-- | Helper for defining the value type of builtin integers.
--
-- @since 1.0.0
integerT :: forall (a :: Type). ValT a
integerT = BuiltinFlat IntegerT

-- | Helper for defining the value type of builtin strings.
--
-- @since 1.0.0
stringT :: forall (a :: Type). ValT a
stringT = BuiltinFlat StringT

-- | Helper for defining the value type of builtin booleans.
--
-- @since 1.0.0
boolT :: forall (a :: Type). ValT a
boolT = BuiltinFlat BoolT

-- | Helper for defining the value type of BLS12-381 G1 curve points.
--
-- @since 1.0.0
g1T :: forall (a :: Type). ValT a
g1T = BuiltinFlat BLS12_381_G1_ElementT

-- | Helper for defining the value type of BLS12-381 G2 curve points.
--
-- @since 1.0.0
g2T :: forall (a :: Type). ValT a
g2T = BuiltinFlat BLS12_381_G2_ElementT

-- | Helper for defining the value type of BLS12-381 multiplication results.
--
-- @since 1.0.0
mlResultT :: forall (a :: Type). ValT a
mlResultT = BuiltinFlat BLS12_381_MlResultT

-- | Helper for defining the value type of the builtin unit type.
--
-- @since 1.0.0
unitT :: forall (a :: Type). ValT a
unitT = BuiltinFlat UnitT

-- Helpers

returnHelper ::
  forall (a :: Type).
  NonEmptyVector (ValT a) ->
  Maybe (ValT a)
returnHelper xs = case NonEmpty.uncons xs of
  (y, ys) ->
    if Vector.length ys == 0
      then pure y
      else Nothing

arrowHelper ::
  forall (a :: Type).
  NonEmptyVector (ValT a) ->
  Maybe (ValT a, CompTBody a)
arrowHelper xs = case NonEmpty.uncons xs of
  (y, ys) -> (y,) . CompTBody <$> NonEmpty.fromVector ys

argsAndResultHelper ::
  forall (a :: Type).
  CompTBody a ->
  (Vector (ValT a), ValT a)
argsAndResultHelper (CompTBody xs) = NonEmpty.unsnoc xs

countHelper ::
  forall (a :: Type).
  Int ->
  CompT a ->
  Maybe (CompTBody a)
countHelper expected (CompT actual xs) = do
  expectedCount <- preview intCount expected
  guard (expectedCount == actual)
  pure xs