Haskell Plutus : Lift, UnsafeLift, and safeLift

📚 Haskell Plutus : Lift, UnsafeLift, and safeLift

Table of Contents

  1. 🔍 Introduction to Lifting

  2. 🧱 Lift Typeclass: The Bridge from Haskell to Plutus Core

  3. ⚠️ UnsafeLift: Risky but Flexible

  4. 🛡️ safeLift: Your Safety Net

  5. 🧪 Example: Custom Type Lifting

  6. 🧠 Quiz Yourself

  7. 📘 Glossary of Terms

  8. 🧩 Summary and Best Practices


1. 🔍 Introduction to Lifting

In Plutus, lifting refers to the process of converting Haskell values into Plutus Core constants so they can be used inside on-chain scripts. This is crucial because:

  • Plutus Core (on-chain) has a limited set of primitive types.

  • Haskell (off-chain) has a much richer type system.

➡️ Lifting makes Haskell data available inside Plutus scripts.


2. 🧱 Lift Typeclass: The Bridge from Haskell to Plutus Core

The Lift typeclass in Plutus defines how a Haskell value is converted (lifted) into a Plutus Core term.

📦 Module

PlutusTx.Lift

🧾 Class Definition

class Lift a where
  lift :: a -> Q (TExp (CompiledCode a))

✅ Usage

You derive Lift for your types so that they can be compiled into Plutus scripts:

{-# LANGUAGE TemplateHaskell #-}

import PlutusTx.Lift

data Color = Red | Blue | Green
PlutusTx.makeLift ''Color

This allows Color to be used in on-chain logic.


3. ⚠️ UnsafeLift: Risky but Flexible

The UnsafeLift typeclass is a more permissive lifting mechanism introduced in newer Plutus versions.

😬 Why “Unsafe”?

  • It doesn’t guarantee that the lifted representation will preserve semantics safely.

  • It’s used internally by Lift and safeLift, but shouldn’t be relied on directly unless you know what you're doing.

🧾 Class

class UnsafeLift a where
  makeUnsafeLift :: a -> CompiledCode a

💡 Usually, you don’t use this directly.


4. 🛡️ safeLift: Your Safety Net

The safeLift function uses the UnsafeLift instance but adds compile-time guarantees using Template Haskell.

✅ Safer Interface

safeLift :: UnsafeLift a => a -> Q (TExp (CompiledCode a))

This is the preferred way to lift Haskell values inside Template Haskell expressions.

💡 Example

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}

import PlutusTx
import PlutusTx.Lift (safeLift)
import GHC.Generics (Generic)

data Token = Token { symbol :: BuiltinByteString, name :: BuiltinByteString }
  deriving (Generic, ToData, FromData)

PlutusTx.makeLift ''Token

myLiftedToken :: Q (TExp (CompiledCode Token))
myLiftedToken = safeLift (Token "ADA" "Coin")

5. 🧪 Example: Custom Type Lifting

Let's walk through lifting a custom record type:

🧾 Type Definition

data User = User
  { userId :: Integer
  , username :: BuiltinByteString
  } deriving (Generic, ToData, FromData)

✅ Derive Lift

PlutusTx.makeLift ''User

🚀 Lift a Value

liftedUser :: Q (TExp (CompiledCode User))
liftedUser = safeLift (User 101 "cardanoFan")

This allows User to be safely passed into on-chain logic.


6. 🧠 Quiz Yourself

✅ Which of the following is the safest way to lift a value?

A. lift value B. makeUnsafeLift value C. safeLift value D. compile value

Answer: ✅ C. safeLift value


7. 📘 Glossary of Terms

Term
Meaning

Lift

A typeclass allowing Haskell values to be embedded in Plutus Core

PlutusTx

Module containing TH-based tools for Plutus

Template Haskell (TH)

Meta-programming in Haskell to generate code at compile-time

CompiledCode

A wrapper type representing compiled Plutus Core

BuiltinByteString

Optimized byte strings for on-chain Plutus logic

Q

The Template Haskell monad used for generating code

TExp

A typed Template Haskell expression

UnsafeLift

An internal or low-level way to lift values without checks

safeLift

A safer way to lift using Template Haskell and UnsafeLift


8. 🧩 Summary and Best Practices

✅ Do
🚫 Don’t

Use safeLift for all Template Haskell-based lifting

Use UnsafeLift unless you really know what you’re doing

Derive Lift with makeLift or makeLiftData

Manually implement Lift unless needed

Keep lifted types small and serializable

Lift large Haskell types with complex dependencies

Use BuiltinByteString, Integer, etc. for compatibility

Use String, Int, or complex types directly


Last updated