-
Notifications
You must be signed in to change notification settings - Fork 2
Compiler Primitives, Builtin Namespaces, and Prelude
Purus provides out-of-the-box support for Plutus Builtins, PlutusData serialization/deserialization functions, and some other miscellaneous utility functions.
Purus supports all Plutus DefaultUni builtins, with types and function names that should be intuitive to those familiar with the Haskell implementation, in a Builtin namespace.
The Builtin module is always implicitly imported. That means that you never need to add
import BuiltinIf you wish to use the modules. For example, this is a valid Purus module:
module Main where
main :: Int
main = Builtin.addInteger 1 2Purus supports the following types under the Builtin namespace:
BuiltinDataBuiltinPairBuiltinList- `BuiltinByteString
BuiltinElementG1BuiltinElementG2BuiltinM1Result
And it supports the full suite of Builtin functions, given here with their Purus types:
addInteger :: Int -> Int -> IntsubtractInteger :: Int -> Int -> IntmultiplyInteger :: Int -> Int -> IntdivideInteger :: Int -> Int -> IntquotientInteger :: Int -> Int -> IntremainderInteger :: Int -> Int -> IntmodInteger :: Int -> Int -> IntequalsInteger :: Int -> Int -> BooleanlessThanInteger :: Int -> Int -> BooleanlessThanEqualsInteger :: Int -> Int -> BooleanappendByteString :: BuiltinByteString -> BuiltinByteString -> BuiltinByteStringconsByteString :: Int -> BuiltinByteString -> BuiltinByteStringsliceByteString :: Int -> Int -> BuiltinByteString -> BuiltinByteStringlengthOfByteString :: BuiltinByteString -> IntindexByteString :: BuiltinByteString -> Int -> IntequalsByteString :: BuiltinByteString -> BuiltinByteString -> BooleanlessThanByteString :: BuiltinByteString -> BuiltinByteString -> BooleanlessThanEqualsByteString :: BuiltinByteString -> BuiltinByteString -> Booleansha2_256 :: BuiltinByteString -> BuiltinByteStringsha3_256 :: BuiltinByteString -> BuiltinByteStringblake2b_256 :: BuiltinByteString -> BuiltinByteStringverifyEd25519Signature :: BuiltinByteString -> BuiltinByteString -> BuiltinByteString -> BooleanverifyEcdsaSecp256k1Signature :: BuiltinByteString -> BuiltinByteString -> BuiltinByteString -> BooleanverifySchnorrSecp256k1Signature :: BuiltinByteString -> BuiltinByteString -> BuiltinByteString -> BooleanappendString :: String -> String -> StringequalsString :: String -> String -> BooleanencodeUtf8 :: String -> BuiltinByteStringdecodeUtf8 :: BuiltinByteString -> String-
ifThenElse :: forall (a :: Type). Boolean -> a -> a -> aSEE NOTE BELOW ON BOOLEANS AND IFTE chooseUnit :: forall (a :: Type). Unit -> a -> atrace :: forall (a :: Type). String -> a -> afstPair :: forall (a :: Type) (b :: Type). BuiltinPair a b -> asndPair :: forall (a :: Type) (b :: Type). BuiltinPair a b -> b-
chooseList :: forall (a :: Type) (b :: Type). BuiltinList a -> b -> bSEE NOTE BELOW ON BUILTINLIST & LIST mkCons :: forall (a :: Type). a -> BuiltinList a -> BuiltinList aheadList :: forall (a :: Type). BuiltinList a -> atailList :: forall (a :: Type). BuiltinList a -> BuiltinList anullList :: forall (a :: Type). BuiltinList a -> BooleanchooseData :: forall (a :: Type). BuiltinData -> a -> a -> a -> a -> aconstrData :: Int -> BuiltinList BuiltinData -> BuiltinDatamapData :: BuiltinList (BuiltinPair BuiltinData BuiltinData) -> BuiltinDatalistData :: BuiltinList BuiltinData -> BuiltinDataiData :: Int -> BuiltinDatabData :: BuiltinByteString -> BuiltinDataunConstrData :: BuiltinData -> BuiltinPair (Int,BuiltinList BuiltinData)unMapData :: BuiltinData -> BuiltinList (BuiltinPair BuiltinData BuiltinData)unListData :: BuiltinData -> BuiltinList BuiltinDataunIData :: BuiltinData -> IntunBData :: BuiltinData -> BuiltinByteStringequalsData :: BuiltinData -> BuiltinData -> BooleanserializeData :: BuiltinData -> BuiltinByteStringmkPairData :: BuiltinData -> BuiltinData -> BuiltinPair BuiltinData BuiltinDatamkNilData :: Unit -> BuiltinList BuiltinDatamkNilPairData :: Unit -> BuiltinList (BuiltinPair BuiltinData BuiltinData)integerToByteString :: Boolean -> Int -> Int -> BuiltinByteStringbyteStringToInteger :: Boolean -> BuiltinByteString -> Int- All of the
bls12*primitives are supported as well, but are omitted here for the sake of brevity.
While this is a technical detail of compiler implementation that most users need not concern themselves with, in certain contexts it may be important to know that *Purus uses the SOP (sum of products) representation for Booleans everywhere to preserve consistency and does not expose any BuiltinBoolean type to users. To interface with the builtins, we insert shim code that converts to and from the SOP boolean to the (internal) BuiltinBoolean.
Additionally, the if-then-else language construct should be preferred over `Builtin.ifThenElse where possible. The language construct does not compile to the same UPLC as the Builtin, and (for reasons that should be familiar to Plutus developers but which are not worth explaining in detail here), you almost certainly want the behavior of the language construct.
Similar to the above remark, BuiltinList and the primitive Purus language List type are not equivalent. The BuiltinList type is only capable of containing elements which are PlutusData-encoded, whereas the language construct is an SOP-encoded algebraic list datatype that can contain Purus values of any Purus type.
The Purus compiler exposes a small set of primitive types:
-
Type(the kind of values) -
Constraint(the kind of constraints) -
Row(the kind of rows of types) -
Function / ->(the function type) -
List(An algebraic data type representation of lists that can contain Purus values of any single type) -
Record(Typically used with the{field1 :: Foo, field2 :: Bar}sugar) -
String(This is NOT a list of characters as in Haskell) Char-
Int(Unbounded, corresponds to Haskell'sInteger, not to Haskell'sInt) -
Boolean(Algebraic data type representation) -
Unit(corresponds to PureScript'sUnitand can be used in Builtin functions directly) -
Delayed(representation of a 'lazy' value)
List and Boolean need some further explanation. Neither of these is equivalent to their Builtin version, and both are defined as algebraic datatypes internally in the compiler. Though these declarations never appear anywhere in a Purus source file, you can regard them as:
data List (a :: Type) = Nil | Cons a (List a)
data Boolean = True | False The fact that these never appear in a source file does not imply that they cannot be used as normal datatypes. You can match on their constructors, construct values of them, etc, in the same way as with every other algebraic datatypes. All of the following expressions are OK:
consOne :: List Int -> List Int
consOne xs = Cons 1 xs
emptyInts :: List Int
emptyInts = Nil
andFromIFTE :: Boolean -> Boolean -> Boolean
andFromIFTE b1 b2 = if b1 then (if b2 then True else False) else False The Purus compiler automatically generates data declarations in the Prim namespace for tuples up to the configurable tuple limit. (You can adjust this limit by modifying the maxTupleSize value in the Language.Purus.Config module. The default is 14, which suffices to cover translation of all of the ledger types).
These declarations look like:
data Tuple1 a = Tuple1 a
data Tuple2 a b = Tuple2 a b
data Tuple3 a b c = Tuple3 a b cand so on. Users can make use of these types directly, and there is nothing particularly special about them (other than the fact that Purus supports them by default out the box, whereas PureScript does not).
In addition to these primitive types, Purus implicitly imports (both qualified as Prim and unqualified) a variety of helper functions under the Prim namespace. Of primary importance to users are the "magical" functions, which are neither Builtin functions defined in Plutus nor PureScript functions that could be defined in a PureScript source file. Those functions are:
delay :: forall (a :: Type). a -> Delayed aforce :: forall (a :: Type). Delayed a -> aerror :: forall (a :: Type). a
These functions work in the manner that you would expect. Under the hood, Delayed a is just
newtype Delayed a = Delayed (Unit -> a)But you cannot match on the constructor to get out the inner function - think of this is as a newtype where the constructor is not exported, and of force/delay as "smart constructors/destructors".
Purus also contains a host of helper functions for writing Plutus programs under the Prim namespace.