Skip to content

Commit

Permalink
Rework the "Using Plutus Tx" section of the user guide
Browse files Browse the repository at this point in the history
  • Loading branch information
zliu41 committed Sep 24, 2024
1 parent 24e3cfc commit f93e076
Show file tree
Hide file tree
Showing 15 changed files with 328 additions and 361 deletions.
31 changes: 0 additions & 31 deletions doc/docusaurus/docs/getting-started-plutus-tx.md

This file was deleted.

33 changes: 15 additions & 18 deletions doc/docusaurus/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,53 +6,50 @@ sidebar_position: 0

## Introduction

Plutus is the native smart contract language for the Cardano ecosystem.
Plutus is the native smart contract language for the Cardano ecosystem.

The Plutus project consists of:
- Plutus Core, the programming language used for scripts on Cardano;
- Tooling and compilers for compiling various intermediate languages into Plutus Core; and
- Plutus Tx, the compiler that compiles the Haskell source code into Plutus Core to form the on-chain part of a contract application.
The Plutus project consists of:
- Plutus Core, the programming language used for scripts on Cardano;
- Tooling and compilers for compiling various intermediate languages into Plutus Core; and
- Plutus Tx, the compiler that compiles the Haskell source code into Plutus Core to form the on-chain part of a contract application.

All of these elements are used in combination to write Plutus Core scripts that run on the Cardano blockchain.

To develop and deploy a smart contract, you also need off-chain code for building transactions, submitting transactions, deploying smart contracts, querying for available UTXOs on the chain, and so on. You may also want a front-end interface for your smart contract for a better user experience.
To develop and deploy a smart contract, you also need off-chain code for building transactions, submitting transactions, deploying smart contracts, querying for available UTXOs on the chain, and so on. You may also want a front-end interface for your smart contract for a better user experience.

Plutus allows all programming to be done from a [single Haskell library](https://plutus.cardano.intersectmbo.org/haddock/latest). This lets developers build secure applications, forge new assets, and create smart contracts in a predictable, deterministic environment with the highest level of assurance. Furthemore, developers don’t have to run a full Cardano node to test their work.
Plutus allows all programming to be done from a [single Haskell library](https://plutus.cardano.intersectmbo.org/haddock/latest). This lets developers build secure applications, forge new assets, and create smart contracts in a predictable, deterministic environment with the highest level of assurance. Furthemore, developers don’t have to run a full Cardano node to test their work.

With Plutus you can:

- Forge new tokens in a lightweight environment,
- Build smart contracts, and
- Support basic multi-sig scripts.

## Getting started with Plutus Tx
See [Getting started with Plutus Tx](getting-started-plutus-tx.md) if you want to jump right in and start a project.

## Intended audience

The intended audience of this documentation includes developers who want to implement smart contracts on the Cardano blockchain.
The intended audience of this documentation includes developers who want to implement smart contracts on the Cardano blockchain.
This involves using Plutus Tx to write scripts, requiring some knowledge of the Haskell programming language.

This guide is also meant for certification companies, certification auditors, and people who need an accurate specification.
This guide is also meant for certification companies, certification auditors, and people who need an accurate specification.
See, for example:

- the [Cardano ledger specification](https://github.com/IntersectMBO/cardano-ledger#cardano-ledger)
- the [Plutus Core specification](https://github.com/IntersectMBO/plutus#specifications-and-design)
- the [public Plutus code libraries](https://plutus.cardano.intersectmbo.org/haddock/latest) generated using Haddock.
- the [public Plutus code libraries](https://plutus.cardano.intersectmbo.org/haddock/latest) generated using Haddock.

## The Plutus repository

The [Plutus repository](https://github.com/IntersectMBO/plutus) includes:
The [Plutus repository](https://github.com/IntersectMBO/plutus) includes:

* the implementation, specification, and mechanized metatheory of Plutus Core
* the Plutus Tx compiler
* the implementation, specification, and mechanized metatheory of Plutus Core
* the Plutus Tx compiler
* the combined documentation, generated using Haddock, for all the [public Plutus code libraries](https://plutus.cardano.intersectmbo.org/haddock/latest), such as `PlutusTx.List`, enabling developers to write Haskell code that can be compiled to Plutus Core.

## Educational resources

The IOG Education Team provides the IOG Academy Haskell Course and the Plutus Pioneer Program (PPP) to attract and train software developers in Plutus.
The IOG Education Team provides the IOG Academy Haskell Course and the Plutus Pioneer Program (PPP) to attract and train software developers in Plutus.

If you are new to Plutus or are looking for additional educational material, please see the following resources:
If you are new to Plutus or are looking for additional educational material, please see the following resources:

- [IOG Academy Haskell Course](https://www.youtube.com/playlist?list=PLNEK_Ejlx3x1D9Vq5kqeC3ZDEP7in4dqb)
- [Plutus Pioneer Program Gitbook](https://iog-academy.gitbook.io/plutus-pioneers-program-fourth-cohort/)
Expand Down
4 changes: 2 additions & 2 deletions doc/docusaurus/docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ If it already has the `INLINABLE` pragma, try adding the GHC flags
`-fno-ignore-interface-pragmas` and `-fno-omit-interface-pragmas`.

If this doesn't resolve the issue, or if the identifier in question isn't directly defined in the code but is produced by GHC optimizations,
ensure that you apply all GHC flags listed in [Compiling Plutus Tx](./using-plutus-tx/compiling-plutus-tx.md).
ensure that you apply all GHC flags listed in [GHC Extensions, Flags and Pragmas](./using-plutus-tx/extensions-flags-pragmas.md).
These flags disable GHC optimizations that can interfere with the plugin, and ensure that unfoldings are neither omitted nor ignored.

If the identifier with missing unfolding is from `base` or invoked by a function from `base`, you should use instead the corresponding function from the `plutus-tx` package.
Expand Down Expand Up @@ -60,7 +60,7 @@ If your expected trace messages are missing, check the following [plugin flags](

### Unexpected Evaluation Failure

It is usually [advisable](./using-plutus-tx/compiling-plutus-tx) to use the `Strict` extension when writing Plutus Tx, which improves performance.
It is usually [advisable](./using-plutus-tx/extensions-flags-pragmas.md) to use the `Strict` extension when writing Plutus Tx, which improves performance.
However, be cautious, as this can result in unexpected evaluation failures.
Consider the following script:

Expand Down
3 changes: 1 addition & 2 deletions doc/docusaurus/docs/using-plutus-tx/_category_.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"label": "Using Plutus Tx",
"position": 40,
"link": {
"type": "generated-index",
"description": "This section guides you through a full spectrum of ideas, from foundational concepts to advanced techniques to help you learn how to use Plutus Tx effectively. "
"type": "generated-index"
}
}

This file was deleted.

This file was deleted.

110 changes: 41 additions & 69 deletions doc/docusaurus/docs/using-plutus-tx/compiling-plutus-tx.md
Original file line number Diff line number Diff line change
@@ -1,95 +1,67 @@
---
sidebar_position: 20
sidebar_position: 10
---

# Compiling Plutus Tx

:::warning
Strictly speaking, while the majority of simple Haskell will work, only a subset of Haskell is supported by the Plutus Tx compiler.
The Plutus Tx compiler will tell you if you are attempting to use an unsupported component.
:::
The Plutus Tx compiler is a GHC plugin, provided by the `plutus-tx-plugin` package.
There are two ways to invoke the plugin: via Template Haskell (preferred) or using a GHC flag.

## GHC Extensions, Flags and Pragmas
Let’s assume we want to compile the following code:

Plutus Tx is a subset of Haskell and is compiled to Untyped Plutus Core by the Plutus Tx compiler, a GHC (Glasgow Haskell Compiler) plugin.
```haskell
module A where

In order to ensure the success and correct compilation of Plutus Tx programs, all Plutus Tx modules (that is, Haskell modules that contain code to be compiled by the Plutus Tx compiler) should use the following GHC extensions, flags and pragmas.
import qualified PlutusTx.Prelude as PlutusTx

### Extensions

Plutus Tx modules should use the `Strict` extension: :
```
{-# LANGUAGE Strict #-}
myPlutusTxCode :: Integer -> Integer
myPlutusTxCode x = x PlutusTx.+ 1
```
Unlike in Haskell, function applications in Plutus Tx are strict.
In other words, when evaluating `(\x -> 42) (3 + 4)` the expression `3 + 4` is evaluated first, before evaluating the function body (`42`), even though `x` is not used in the function body.
The `Strict` extension ensures that let bindings and patterns are also (by default) strict, for instance, evaluating `let x = 3 + 4 in 42` evaluates `3 + 4` first, even though `x` is not used.

Bang patterns and lazy patterns can be used to explicitly specify whether a let binding is strict or non-strict, as in `let !x = 3 + 4 in 42` (strict) and `let ~x = 3 + 4 in 42` (non-strict).
At this time, it is not possible to make function applications non-strict: `(\(~x) -> 42) (3 + 4)` still evaluates `3 + 4` strictly.
> :pushpin: **NOTE**
>
> There are some GHC extensions, flags and pragmas recommended for modules containing Plutus Tx code, but these are omitted here.
> You can find more information in [GHC Extensions, Flags and Pragmas](./extensions-flags-pragmas.md).
Making let bindings strict by default has the following advantages:
## Compiling Using Template Haskell (Preferred)

- It makes let bindings and function applications semantically equivalent. For example, `let x = 3 + 4 in 42` has the same semantics as `(\x -> 42) (3 + 4)`.
This is what one would come to expect, as it is the case in most other programming languages, regardless of whether the language is strict or non-strict.
- Untyped Plutus Core programs, which are compiled from Plutus Tx, are not evaluated lazily (unlike Haskell), that is, there is no memoization of the results of evaluated expressions.
Thus using non-strict bindings can cause an expression to be inadvertently evaluated for an unbounded number of times.
Consider `let x = <expensive> in \y -> x + y`.
If `x` is non-strict, `<expensive>` will be evalutated every time `\y -> x + y` is applied to an argument, which means it can be evaluated 0 times, 1 time, 2 times, or any number of times (this is not the case if lazy evaluation was employed).
On the other hand, if `x` is strict, it is always evaluated once, which is at most one more time than what is necessary.
Here's how to compile `myPlutusTxCode` using Template Haskell:

### Flags
```haskell
{-# LANGUAGE TemplateHaskell #-}
module B where

GHC has a variety of optimization flags, many of which are on by default.
Although Plutus Tx is, syntactically, a subset of Haskell, it has different semantics and a different evaluation strategy (Haskell: non-strict semantics, call by need; Plutus Tx: strict semantics, call by value). As a result, some GHC optimizations are not helpful for Plutus Tx programs, and can even be harmful, in the sense that it can make Plutus Tx programs less efficient, or fail to be compiled.
An example is the full laziness optimization, controlled by GHC flag `-ffull-laziness`, which floats let bindings out of lambdas whenever possible.
Since Untyped Plutus Core does not employ lazy evaluation, the full laziness optimization is usually not beneficial, and can sometimes make a Plutus Tx program more expensive.
Conversely, some GHC features must be turned on in order to ensure Plutus Tx programs are compiled successfully.
import PlutusTx.Code (CompiledCode)
import PlutusTx.TH (compile)

All Plutus Tx modules should use the following GHC flags:
```
-fno-ignore-interface-pragmas
-fno-omit-interface-pragmas
-fno-full-laziness
-fno-spec-constr
-fno-specialise
-fno-strictness
-fno-unbox-strict-fields
-fno-unbox-small-strict-fields
myPlutusTxCodeCompiled :: CompiledCode (Integer -> Integer)
myPlutusTxCodeCompiled = $$(compile [|| myPlutusTxCode ||])
```

`-fno-ignore-interface-pragmas` and `-fno-omit-interface-pragmas` ensure unfoldings of Plutus Tx functions are available.
The rest are GHC optimizations that are generally bad for Plutus Tx, and should thus be turned off.
Under the hood, it uses [`addCorePlugin`](https://hackage.haskell.org/package/template-haskell/docs/Language-Haskell-TH-Syntax.html#v:addCorePlugin) from the `template-haskell` package to install the plugin into the compilation pipeline.

These flags can be specified either in a Haskell module, for example:
```
{-# OPTIONS_GHC -fno-ignore-interface-pragmas #-}
```
or in a build file.
For example, if your project is built using Cabal, you can add the flags to the `.cabal` files, like so:
You can then compile module `B` as you would any regular Haskell module.
The resulting `CompiledCode` contains the UPLC code, and also includes PIR for debugging.

> ghc-options:
>
> : -fno-ignore-interface-pragmas
Template Haskell is a complicated piece of machinery, but as you can see, you need to understand almost none of it for the purpose of compiling Plutus Tx.

### Plutus Tx compiler options
This method is preferred since it can leverage Template Haskell's [`location`](https://hackage.haskell.org/package/template-haskell/docs/Language-Haskell-TH.html#v:location) function to pass the location of `$$(compile [|| ... ||])` to the plugin, which is used in error messages.

> :pushpin: **NOTE**
>
> This section only covers GHC flags, not Plutus Tx compiler flags.
> A number of options can be passed to the Plutus Tx compiler.
> See [Reference > Plutus Tx Compiler Options](../delve-deeper/plutus-tx-compiler-options.md) for details.
## Compiling Using GHC Flag

An alternative way to compile `myPlutusTxCode` is by using the `-fplugin` GHC flag, which installs a plugin into the pipeline.
Use this flag with the `plc` function:

### Pragmas
```haskell
{-# OPTIONS_GHC -fplugin PlutusTx.Plugin #-}
module B where

All functions and methods should have the `INLINEABLE` pragma, so that their unfoldings are made available to the Plutus Tx compiler.
import Data.Proxy
import PlutusTx.Code (CompiledCode)
import PlutusTx.Plugin (plc)

The `-fexpose-all-unfoldings` flag also makes GHC expose all unfoldings, but unfoldings exposed this way can be more optimized than unfoldings exposed via `INLINEABLE`.
In general, we do not want GHC to perform optimizations, since GHC optimizes a program based on the assumption that it has non-strict semantics and is evaluated lazily (call by need), which is not true for Plutus Tx programs.
Therefore, `INLINEABLE` is preferred over `-fexpose-all-unfoldings`, even though the latter is simpler.
myPlutusTxCodeCompiled :: CompiledCode (Integer -> Integer)
myPlutusTxCodeCompiled = plc (Proxy @"location info") myPlutusTxCode
```

`-fexpose-all-unfoldings` can be useful for functions that are generated by GHC and do not have the `INLINEABLE` pragma.
`-fspecialise` and `-fspec-constr` are two examples of optimizations that can generate such functions.
The most reliable solution, however, is to simply turn these optimizations off.
Another option is to bump `-funfolding-creation-threshold` to make it more likely for GHC to retain unfoldings for functions without the `INLINEABLE` pragma.
`-fexpose-all-unfoldings` should be used as a last resort.
The `-fplugin` flag must be used on every module that invokes `plc`.
47 changes: 47 additions & 0 deletions doc/docusaurus/docs/using-plutus-tx/differences-from-haskell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
sidebar_position: 15
---

# Differences From Haskell

## Strictness

### Function Applications

Unlike in Haskell, function applications in Plutus Tx are strict.
In other words, when evaluating `(\x -> 42) (3 + 4)` the expression `3 + 4` is evaluated first, before evaluating the function body (`42`), even though `x` is not used in the function body.

Using lazy patterns on function parameters does not change this behavior: : `(\(~x) -> 42) (3 + 4)` still evaluates `3 + 4` strictly.
At this time, it is not possible to make function applications non-strict in Plutus Tx.

### Bindings

Bindings in Plutus Tx are by default non-strict, but they can be made strict via the bang pattern (`!`), as in `let !x = 3 + 4 in 42`.
Conversely, in modules with the `Strict` language extension on, bindings are by default strict, but they can be made non-strict via the lazy pattern (`~`), as in `let ~x = 3 + 4 in 42`.

> :pushpin: **NOTE**
>
> It is important to note that the UPLC evaluator does not perform lazy evaluation, which means a non-strict binding will be evaluated each time it is used, rather than at most once.
## Supported Haskell Features

The Plutus Tx compiler provides good support for basic Haskell features, including regular algebraic data types, type classes, higher order functions, parametric polymorphism, etc.
However, it doesn’t support many of Haskell’s more advanced features.
A good rule of thumb for writing Plutus Tx is to stick with simple Haskell (which is also typically good advice for Haskell development in general).

Some notable Haskell features _not_ supported by Plutus Tx include:

- Many functions and methods in [`base`](https://hackage.haskell.org/package/base).
Use the counterparts in the [`plutus-tx`](https://plutus.cardano.intersectmbo.org/haddock/latest/plutus-tx/) library instead.
This also means most Haskell third-party libraries are not supported, unless the library is developed specifically for Plutus Tx.
- Mutually recursive data types.
- Type families
- Existential types
- GADTs
- IO and FFI

Use of these features often leads the Plutus Tx compiler to report an "Unsupported feature" error, though you may sometimes get a different error.

Since the Plutus Tx compiler is a GHC plugin that runs after GHC's type checking, unsupported Haskell features cannot be detected at type checking time.
As a result, it's unlikely that an IDE will be able to report these errors.
You will need to compile the Haskell module to determine if everything is compiled successfully.
Loading

0 comments on commit f93e076

Please sign in to comment.