Types
As we saw in Concepts plu-ts
is an eDSL embedded in Typescript, as such we have two type systems; the Typescript one and the plu-ts
one
Typescript Types
Term
is a Typescript type defined inplu-ts
.- Every value in
plu-ts
is aTerm
. In Typescript, we say each value extends Term (in the same way that "Dog" extends "Mammal"). - A
Term
also keeps track of the type of the value it holds.
The possible types a Term
can keep track of are defined in PTypes, and listed here:
PUnit
a unique value that has no real meaning; you can see it asplu-ts
version ofundefined
ornull
in TypescriptPInt
a signed integer that can be as big as you wantPBool
a boolean valuePByteString
equivalent of aBuffer
or aUint8Array
PString
equivalent of the Typescriptstring
PData
equivalent of theobject
type in Typescript (it is the low level representation ofPStruct
s that we'll cover in a moment, so you usually won't usePData
)PList<PType>
equivalent of an Array in Typescript; note that all the elements in the list must be of the samePType
PPair<PType1, PType2>
equivalent of a Typescript tuple ([ type1 , type2 ]
)PDelayed<PType>
a delayed computation that returns a value of typePType
; the computation can be run by passing the delayed value to thepforce
functionPLam<PInput, POutput>
a function that takes one single argument of typePInput
and returns something of typePOutput
PFn<[ PInput_0 , ...PType[] ],POutput>
a function that takes multiple arguments (at least one) and returns something of typePOutput
PAlias<PType>
just an alias of the provided type; it behaves exactly like the Types of its argument, soPAlias<PInt>
is equivalent to aPInt
. This is useful for keeping track of a different meaning the type might have.PStruct<{...}>
an abstraction overPData
, useful to construct more complex data structures.PSop<{...}>
essentially similar toPStruct
; works in Plutus V3 as constructor is introduced.
plu-ts
Types
plu-ts
would not be a strongly typed language if limited to Typescript types, because the types of Typescript are only useful during compilation to javascript; and then everything is untyped!
Typescript can be compiled to Javascript. When this happens, the resulting Javascript is untyped!
For this reason plu-ts
implements its own type through some constants and functions that can be imported.
In the same order of above, the plu-ts
equivalents are:
PUnit
->unit
PInt
->int
PBool
->bool
PByteString
->bs
PString
->str
PData
->data
PList
->list( type )
PPair
->pair( type1, type2 )
PDelayed
->delayed( type )
PLam
->lam( from, to )
PFn
->fn([ ...inputs ], output )
- aliases types and structs types will be retrieved by the
type
static property of the classes (explained in the dedicated section for aliases and structs)
plu-ts
generics
As we know, Typescript gives us the possibility to define polymorphic types trough generics.
In a way you could see generics as functions that take a type as input and returns another type.
Fortunately for us, plu-ts
types are just values when seen from the Typescript world, so we can have some sort of generic in plu-ts
too!
To see how, let's try to define the previous polymorphic types using the generic method:
const anyPlutsFunction = ( a: TermType, b: TermType ) => lam( a, b );
const identityFunctionType = ( a: TermType ) => lam( a, a );
const mkPairType = ( a: TermType, b: TermType ) => fn([ a, b ], pair( a, b ) )
Polymorphic types defined as functions also have the advantage of being fully defined once the type arguments are passed.
This works great, but there's still a problem though... Typescript isn't able to infer these types!
This is because TermType
is the generic type for all plu-ts
types, so when Typescript tries to infer the type of the term, it sees the most generic type of all, and once again thinks we want to use the anonymous PType
.
To finally solve this problem we need to make the functions generic too:
function anyFunction<A extends TermType, B extends TermType>( a: A, b: B )
{
return lam( a, b )
};
function identityFunctionType<A extends TermType>( a: A )
{
return lam( a, a )
};
function mkPairType<A extends TermType, B extends TermType>( a: A, b: B )
{
return fn([ a, b ], pair( a, b ) )
};
This way Typescript first infers the specific TermType
we are passing, and with that is able to determine the exact type of the Term.
Generics will turn useful especially in the case where the type requires a type variable as return type, a notable example is pif
which is of type bool -> a -> a -> a
.