This document serves as a guide for working with Plutus Core builtin lists. It's primarily meant for Pluto users.
Plutarch users no longer need to know the information discussed here. Prefer PListLike
class methods instead - which is implemented by PBuiltinList
. The information may, however, benefit Plutarch developers/contributors.
Note: If you spot any mistakes/have any related questions that this guide lacks the answer to, please don't hesitate to raise an issue. The goal is to have high quality documentation for Pluto and Plutarch users!
For using and operating on builtin lists, all you need are a few builtin functions. These are discussed below.
Love it or hate it, here's the well known head
synonym. A "synonym" in the sense that it shares all the perks and flaws of the Prelude.head
we know and love (or hate)!
(I'm trying to hint at the fact that HeadList
is indeed a partial function)
Its type looks like- HeadList :: forall a. BuiltinList a -> a
. See that type variable? That means it takes one force. But you already knew that. You force it once, and pass in a builtin list, and you get the first element of the list (or a scary error if the list is empty). Simple!
You can call HeadList
as you would any other function, just make sure you force it!
! HeadList xs
Where xs
is a builtin list.
Create a Plutarch a synonym to HeadList
using punsafeBuiltin
-
pheadBuiltin :: Term s (PBuiltinList a :--> a)
pheadBuiltin = phoistAcyclic $ pforce $ punsafeBuiltin PLC.HeadList
You would use it like any other Plutarch level function.
pheadBuiltin # xs
Where xs
is of type PBuiltinList a
.
Here's HeadList
's twin, TailList
. Its type looks like TailList :: forall a. BuiltinList a -> BuiltinList a
. It also takes one force. You force it once, and pass in a builtin list, and you get its tail (or an error)!
You can call TailList
as you would any other function, just make sure you force it!
! TailList xs
Where xs
is a builtin list.
Create a Plutarch a synonym to TailList
using punsafeBuiltin
-
ptailBuiltin :: Term s (PBuiltinList a :--> PBuiltinList a)
ptailBuiltin = phoistAcyclic $ pforce $ punsafeBuiltin PLC.TailList
You would use it like any other Plutarch level function.
ptailBuiltin # xs
Where xs
is of type PBuiltinList a
.
This is synonymous to null
. The function to check whether or not a list is empty. Its type looks like NullList :: forall a. BuiltinList a -> BuiltinBool
. It takes one force, a builtin list argument, and returns True
if the list is empty, False
otherwise.
You can call NullList
as you would any other function, just make sure you force it!
! NullList xs
Where xs
is a builtin list.
Create a Plutarch a synonym to NullList
using punsafeBuiltin
-
pnullBuiltin :: Term s (PBuiltinList a :--> PBool)
pnullBuiltin = phoistAcyclic $ pforce $ punsafeBuiltin PLC.NullList
You would use it like any other Plutarch level function.
pnullBuiltin # xs
Where xs
is of type PBuiltinList a
.
You probably expected this one from a mile (or kilometer) away. It's :
, cons!
Its type looks like MkCons :: forall a. a -> BuiltinList a -> BuiltinList a
. It takes one force, an element, and a builtin list that contains elements of the same type, and returns a new list with the element prepended to the old list.
You can call MkCons
as you would any other function, just make sure you force it!
! MkCons x xs
Where x
is of type a
, and xs
is a builtin list of elements of type a
.
Create a Plutarch a synonym to MkCons
using punsafeBuiltin
-
pconsBuiltin :: Term s (a -> PBuiltinList a :--> PBuiltinList a)
pconsBuiltin = phoistAcyclic $ pforce $ punsafeBuiltin PLC.MkCons
You would use it like any other Plutarch level function.
pconsBuiltin # x # xs
Where x
is of type a
, and xs
is of type PBuiltinList a
.
Catamorphisms! Everyone loves those right? If you wrote this function in Haskell, it would look like-
chooseList :: [a] -> b -> b -> b
chooseList [] x _ = x
chooseList (_:_) _ y = y
It takes two forces, a builtin list, and two branches (both strictly evaluated, as usual), and yields a branch corresponding to the builtin list representation. If the list is empty, it returns the first branch, otherwise, the second branch.
Aside: Wanna know something cool? In Pluto, you actually don't have to make the two branches,
a
andb
, the same type.
You can call ChooseList
as you would any other function, just make sure you force it!
! ! ChooseList xs a b
Where xs
is a builtin list, and a
and b
are the two branches.
Create a Plutarch a synonym to ChooseList
using punsafeBuiltin
-
pchooseListBuiltin :: Term s (PBuiltinList a :--> b :--> b :--> b)
pchooseListBuiltin = phoistAcyclic $ pforce $ pforce $ punsafeBuiltin PLC.ChooseList
You would use it like any other Plutarch level function.
pchooseListBuiltin # xs # a # b
Where xs
is of type PBuiltinList a
, and a
and b
are the two branches of type b
.
You're probably wondering, "All those functions above take in builtin lists. That's cool, but where can I get me one of these builtin lists you speak of?". Good question! Here's the first of several ways to obtain a builtin list.
MkNilData
takes in a BuiltinUnit
and returns an empty list (nil
) of BuiltinData
/Data
. So, its return type is like BuiltinList Data
.
More often than not, you'll actually be working on builtin lists of Data
. You rarely need to use builtin lists with elements of other type (except BuiltinPair
). Convenient!
You can call MkNilData
as you would any other function.
MkNilData ()
It will give you a nil
list of Data
elements.
Create a Plutarch a synonym to MkNilData
using punsafeBuiltin
-
pnilDataBuiltin :: Term s (PBuiltinList PData)
pnilDataBuiltin = punsafeBuiltin PLC.MkNilData # pcon PUnit
I went ahead and applied the ()
(unit) onto the function. So it's just a nil
(of Data
elements) now. Instead of a function that returns nil
(of Data
elements).
Here's the function to build a builtin list of BuiltinPair Data Data
elements! This is the second most common element type for builtin lists you'll be using on chain. It works much like MkNilData
, pass it a BuiltinUnit
, and it will yield an empty list (nil
) of BuiltinPair Data Data
. So, its return type is like BuiltinList (BuiltinPair Data Data)
.
You can call MkNilPairData
as you would any other function.
MkNilPairData ()
It will give you a nil
list of BuiltinPair Data Data
elements.
Create a Plutarch a synonym to MkNilPairData
using punsafeBuiltin
-
pnilPairDataBuiltin :: Term s (PBuiltinList (BuiltinPair PData PData))
pnilPairDataBuiltin = punsafeBuiltin PLC.MkNilPairData # pcon PUnit
I went ahead and applied the ()
(unit) onto the function. So it's just a nil
(of BuiltinPair Data Data
elements) now. Instead of a function that returns nil
(of BuiltinPair Data Data
elements).
Let me read your mind. You're thinking, "Wait, you didn't tell me how to build a list of integers/bytestrings/strings/other pairs/lists!". The truth is that you won't really need to build them most of the time. But you can! You just need to build a constant directly.
This is not currently possible in Pluto. But if you're using Plutarch, read constant building and PLift
.
Here's how to make the nil for builtin lists of integers in Plutarch-
pnilIntBuiltin :: Term s (PBuiltinList PInteger)
pnilIntBuiltin = pconstant []
You can also build a somewhat polymorphic nil
-
pnilIntBuiltin :: PLC.Contains PLC.DefaultUni (PHaskellType a) => Term s (PBuiltinList a)
pnilIntBuiltin = pconstant []
It only works for DefaultUni
element types, however (i.e built in).
There is no truly polymorphic nil
in Plutus Core. When you create a nil
constant - you always explicitly choose the element type (when using punsafeConstant
- pconstant
does this under the hood as well). A nil
of DefaultUniData
element type (built using Some $ ValueOf (DefaultUniList DefaultUniData) []
) WILL NOT work with builtin lists of other element types. Don't ignore that DefaultUniList DefaultUniData
- it is preserved in the runtime!