-
Notifications
You must be signed in to change notification settings - Fork 476
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rework the "Using Plutus Tx" section of the user guide
- Loading branch information
Showing
15 changed files
with
328 additions
and
361 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 0 additions & 8 deletions
8
doc/docusaurus/docs/using-plutus-tx/advanced-plutus-tx-concepts/_category_.json
This file was deleted.
Oops, something went wrong.
9 changes: 0 additions & 9 deletions
9
.../using-plutus-tx/advanced-plutus-tx-concepts/triggering-a-validation-failure.md
This file was deleted.
Oops, something went wrong.
110 changes: 41 additions & 69 deletions
110
doc/docusaurus/docs/using-plutus-tx/compiling-plutus-tx.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
47
doc/docusaurus/docs/using-plutus-tx/differences-from-haskell.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
Oops, something went wrong.