stateio

package
v2.1.13 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 17, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package stateio provides a functional programming abstraction that combines stateful computations with side effects.

Fantasy Land Specification

This is a monad transformer combining:

Implemented Fantasy Land algebras:

StateIO

StateIO[S, A] represents a computation that:

  • Manages state of type S (State monad)
  • Performs side effects (IO monad)
  • Produces a value of type A

The type is defined as:

StateIO[S, A] = Reader[S, IO[Pair[S, A]]]

This is particularly useful for:

  • Stateful computations with side effects
  • Managing application state while performing IO operations
  • Composing operations that need both state management and effectful computation

Core Operations

Construction:

  • Of: Create a computation with a pure value
  • FromIO: Lift an IO computation into StateIO

Transformation:

  • Map: Transform the value within the computation
  • Chain: Sequence dependent computations (monadic bind)

Combination:

  • Ap: Apply a function in a context to a value in a context

Kleisli Arrows:

  • FromIOK: Lift an IO-returning function to a Kleisli arrow

Do Notation (Monadic Composition):

  • Do: Start a do-notation chain
  • Bind: Bind a value from a computation
  • BindTo: Bind a value to a simple constructor
  • Let: Compute a derived value
  • LetTo: Set a constant value
  • ApS: Apply in sequence (for applicative composition)
  • BindL/ApSL/LetL/LetToL: Lens-based variants for working with nested structures

Example Usage

type AppState struct {
    RequestCount int
    LastError    error
}

// A computation that manages state and performs IO
func incrementCounter(data string) StateIO[AppState, string] {
    return func(state AppState) IO[Pair[AppState, string]] {
        return func() Pair[AppState, string] {
            // Update state.RequestCount
            // Perform IO operations
            newState := AppState{RequestCount: state.RequestCount + 1}
            result := "processed: " + data
            return pair.MakePair(newState, result)
        }
    }
}

// Compose operations using do-notation
type Result struct {
    result string
    count  int
}

computation := function.Pipe3(
    Do[AppState](Result{}),
    Bind(
        func(result string) func(Result) Result {
            return func(r Result) Result { return Result{result: result, count: r.count} }
        },
        func(r Result) StateIO[AppState, string] {
            return incrementCounter("data")
        },
    ),
    Map[AppState](func(r Result) string { return r.result }),
)

// Execute with initial state
initialState := AppState{RequestCount: 0}
outcome := computation(initialState)() // Returns Pair[AppState, string]

Monad Laws

StateIO satisfies the monad laws:

  • Left Identity: Of(a) >>= f ≡ f(a)
  • Right Identity: m >>= Of ≡ m
  • Associativity: (m >>= f) >>= g ≡ m >>= (x => f(x) >>= g)

Where >>= represents the Chain operation (monadic bind).

These laws ensure that StateIO computations compose predictably and that the order of composition doesn't affect the final result (beyond the order of effects and state updates).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Applicative

func Applicative[
	S, A, B any,
]() applicative.Applicative[A, B, StateIO[S, A], StateIO[S, B], StateIO[S, func(A) B]]

Applicative returns an Applicative instance for StateIO. The Applicative typeclass provides 'Of', 'Map', and 'Ap' operations for applicative-style composition of StateIO computations.

Example:

app := Applicative[AppState, int, string]()
fab := Of[AppState](func(x int) string { return strconv.Itoa(x) })
fa := Of[AppState](42)
result := app.Ap(fa)(fab)

func ApplicativeMonoid added in v2.1.6

func ApplicativeMonoid[S, A any](m M.Monoid[A]) M.Monoid[StateIO[S, A]]

ApplicativeMonoid lifts a monoid into the StateIO applicative functor context.

This function creates a monoid for StateIO[S, A] values given a monoid for the base type A. It uses the StateIO monad's applicative operations (Of, MonadMap, MonadAp) to lift the monoid operations into the StateIO context, allowing you to combine stateful computations with side effects that produce monoidal values.

The resulting monoid combines StateIO computations by:

  1. Threading the state through both computations sequentially
  2. Executing the IO effects of both computations in sequence
  3. Combining the produced values using the underlying monoid's Concat operation
  4. Returning a new StateIO computation with the combined value and final state

The empty element is a StateIO computation that returns the underlying monoid's empty value without modifying the state or performing any side effects.

This is particularly useful for:

  • Accumulating results across multiple stateful computations with side effects
  • Building complex state transformations that aggregate values while performing IO
  • Combining independent stateful operations that produce monoidal results

Type Parameters:

  • S: The state type that is threaded through the computations
  • A: The value type that has a monoid structure

Parameters:

  • m: A monoid for the base type A that defines how to combine values

Returns:

  • A Monoid[StateIO[S, A]] that combines stateful IO computations using the base monoid

The resulting monoid satisfies the standard monoid laws:

  • Associativity: Concat(Concat(s1, s2), s3) = Concat(s1, Concat(s2, s3))
  • Left identity: Concat(Empty(), s) = s
  • Right identity: Concat(s, Empty()) = s

Example with integer addition:

import (
    N "github.com/IBM/fp-go/v2/number"
    "github.com/IBM/fp-go/v2/io"
    "github.com/IBM/fp-go/v2/pair"
)

type Counter struct {
    count int
}

// Create a monoid for StateIO[Counter, int] using integer addition
intAddMonoid := N.MonoidSum[int]()
stateIOMonoid := stateio.ApplicativeMonoid[Counter](intAddMonoid)

// Create two stateful IO computations
s1 := stateio.Of[Counter](5)  // Returns 5, state unchanged
s2 := stateio.Of[Counter](3)  // Returns 3, state unchanged

// Combine them using the monoid
combined := stateIOMonoid.Concat(s1, s2)
result := combined(Counter{count: 10})()
// result = Pair{head: Counter{count: 10}, tail: 8}  // 5 + 3

// Empty element
empty := stateIOMonoid.Empty()
emptyResult := empty(Counter{count: 10})()
// emptyResult = Pair{head: Counter{count: 10}, tail: 0}

Example with string concatenation and state modification:

import (
    S "github.com/IBM/fp-go/v2/string"
    "github.com/IBM/fp-go/v2/io"
)

type Logger struct {
    logs []string
}

strMonoid := S.Monoid
stateIOMonoid := stateio.ApplicativeMonoid[Logger](strMonoid)

// Stateful IO computation that logs and returns a message
logMessage := func(msg string) stateio.StateIO[Logger, string] {
    return func(s Logger) io.IO[pair.Pair[Logger, string]] {
        return func() pair.Pair[Logger, string] {
            newState := Logger{logs: append(s.logs, msg)}
            return pair.MakePair(newState, msg)
        }
    }
}

s1 := logMessage("Hello")
s2 := logMessage(" World")

// Combine the computations - both log entries are added, messages concatenated
combined := stateIOMonoid.Concat(s1, s2)
result := combined(Logger{logs: []string{}})()
// result.head.logs = ["Hello", " World"]
// result.tail = "Hello World"

Example demonstrating monoid laws:

intAddMonoid := N.MonoidSum[int]()
m := stateio.ApplicativeMonoid[Counter](intAddMonoid)

s1 := stateio.Of[Counter](1)
s2 := stateio.Of[Counter](2)
s3 := stateio.Of[Counter](3)

initialState := Counter{count: 0}

// Associativity
left := m.Concat(m.Concat(s1, s2), s3)
right := m.Concat(s1, m.Concat(s2, s3))
// Both produce: Pair{head: Counter{count: 0}, tail: 6}

// Left identity
leftId := m.Concat(m.Empty(), s1)
// Produces: Pair{head: Counter{count: 0}, tail: 1}

// Right identity
rightId := m.Concat(s1, m.Empty())
// Produces: Pair{head: Counter{count: 0}, tail: 1}

func Eq

func Eq[
	S, A any](eqr eq.Eq[IO[Pair[S, A]]]) func(S) eq.Eq[StateIO[S, A]]

Eq constructs an equality checker for StateIO values. It takes an equality checker for IO[Pair[S, A]] and returns a function that, given an initial state S, produces an equality checker for StateIO[S, A].

Two StateIO values are considered equal if, when executed with the same initial state, they produce equal IO[Pair[S, A]] results.

Example:

eqIO := io.FromStrictEquals[Pair[AppState, int]]()
eqStateIO := Eq[AppState, int](eqIO)
initialState := AppState{}
areEqual := eqStateIO(initialState).Equals(stateIO1, stateIO2)

func FromStrictEquals

func FromStrictEquals[
	S, A comparable]() func(S) eq.Eq[StateIO[S, A]]

FromStrictEquals constructs an equality checker for StateIO values where both the state S and value A are comparable types.

This is a convenience function that uses Go's built-in equality (==) for comparison. It returns a function that, given an initial state, produces an equality checker for StateIO[S, A].

Example:

eqStateIO := FromStrictEquals[AppState, int]()
initialState := AppState{}
areEqual := eqStateIO(initialState).Equals(stateIO1, stateIO2)

func Functor

func Functor[
	S, A, B any,
]() functor.Functor[A, B, StateIO[S, A], StateIO[S, B]]

Functor returns a Functor instance for StateIO. The Functor typeclass provides the 'Map' operation to transform values within the StateIO context.

Example:

f := Functor[AppState, int, string]()
result := f.Map(strconv.Itoa)(Of[AppState](42))

func Monad

func Monad[
	S, A, B any,
]() monad.Monad[A, B, StateIO[S, A], StateIO[S, B], StateIO[S, func(A) B]]

Monad returns a Monad instance for StateIO. The Monad typeclass provides 'Of', 'Map', 'Chain', and 'Ap' operations for monadic composition of StateIO computations.

Example:

m := Monad[AppState, int, string]()
result := m.Chain(func(x int) StateIO[AppState, string] {
    return Of[AppState](strconv.Itoa(x))
})(Of[AppState](42))

func Pointed

func Pointed[
	S, A any,
]() pointed.Pointed[A, StateIO[S, A]]

Pointed returns a Pointed instance for StateIO. The Pointed typeclass provides the 'Of' operation to lift pure values into the StateIO context.

Example:

p := Pointed[AppState, int]()
result := p.Of(42)

Types

type Either

type Either[E, A any] = either.Either[E, A]

Either represents a value that can be either a Left (error) or Right (success).

type Endomorphism

type Endomorphism[A any] = endomorphism.Endomorphism[A]

Endomorphism represents a function from A to A.

func ApSL

func ApSL[ST, S, T any](
	lens Lens[S, T],
	fa StateIO[ST, T],
) Endomorphism[StateIO[ST, S]]

ApSL is a lens-based variant of ApS for working with nested structures. It uses a lens to focus on a specific field in the accumulator state, making it easier to update nested fields without manual destructuring.

Example:

nameLens := lens.Prop[Result, string]("name")
result := function.Pipe2(
    Do[AppState](Result{}),
    ApSL(nameLens, Of[AppState]("John")),
)

func BindL

func BindL[ST, S, T any](
	lens Lens[S, T],
	f Kleisli[ST, T, T],
) Endomorphism[StateIO[ST, S]]

BindL is a lens-based variant of Bind for working with nested structures. It uses a lens to focus on a specific field in the accumulator state, allowing you to update that field based on a computation that depends on its current value.

Example:

counterLens := lens.Prop[Result, int]("counter")
result := function.Pipe2(
    Do[AppState](Result{counter: 0}),
    BindL(counterLens, func(n int) StateIO[AppState, int] {
        return Of[AppState](n + 1)
    }),
)

func LetL

func LetL[ST, S, T any](
	lens Lens[S, T],
	f Endomorphism[T],
) Endomorphism[StateIO[ST, S]]

LetL is a lens-based variant of Let for working with nested structures. It uses a lens to focus on a specific field in the accumulator state, allowing you to update that field using a pure function.

Example:

counterLens := lens.Prop[Result, int]("counter")
result := function.Pipe2(
    Do[AppState](Result{counter: 5}),
    LetL(counterLens, N.Mul(2)),
)

func LetToL

func LetToL[ST, S, T any](
	lens Lens[S, T],
	b T,
) Endomorphism[StateIO[ST, S]]

LetToL is a lens-based variant of LetTo for working with nested structures. It uses a lens to focus on a specific field in the accumulator state, allowing you to set that field to a constant value.

Example:

statusLens := lens.Prop[Result, string]("status")
result := function.Pipe2(
    Do[AppState](Result{}),
    LetToL(statusLens, "active"),
)

type IO

type IO[A any] = io.IO[A]

IO represents a computation that performs side effects and produces a value of type A.

type Kleisli

type Kleisli[S, A, B any] = Reader[A, StateIO[S, B]]

Kleisli represents a Kleisli arrow for StateIO. It's a function from A to StateIO[S, B], enabling composition of stateful, effectful computations.

Kleisli arrows are used for:

  • Chaining dependent computations
  • Building pipelines of stateful operations
  • Monadic composition with Chain/Bind operations

func FromIOK

func FromIOK[S, A, B any](f func(A) IO[B]) Kleisli[S, A, B]

FromIOK lifts an IO-returning function into a Kleisli arrow for StateIO. This is useful for composing functions that return IO actions with StateIO computations.

Example:

readFile := func(path string) IO[string] { ... }
kleisli := FromIOK[AppState](readFile)
// kleisli can now be used with Chain

func WithResource

func WithResource[A, S, RES, ANY any](
	onCreate StateIO[S, RES],
	onRelease Kleisli[S, RES, ANY],
) Kleisli[S, Kleisli[S, RES, A], A]

WithResource provides safe resource management for StateIO computations. It ensures that resources are properly acquired and released, even if errors occur.

The function takes:

  • onCreate: A StateIO computation that creates/acquires the resource
  • onRelease: A Kleisli arrow that releases the resource (receives the resource, returns any value)

It returns a Kleisli arrow that takes a resource-using computation and ensures proper cleanup.

The pattern follows the bracket pattern (acquire-use-release):

  1. Acquire the resource using onCreate
  2. Use the resource with the provided computation
  3. Release the resource using onRelease (guaranteed to run)

Example:

// Create a file resource
openFile := func(s AppState) IO[Pair[AppState, *os.File]] {
    return io.Of(pair.MakePair(s, file))
}

// Release the file resource
closeFile := func(f *os.File) StateIO[AppState, error] {
    return FromIO[AppState](io.Of(f.Close()))
}

// Use the resource safely
withFile := WithResource[string, AppState, *os.File, error](
    openFile,
    closeFile,
)

// Apply to a computation that uses the file
result := withFile(func(f *os.File) StateIO[AppState, string] {
    // Use file f here
    return Of[AppState]("data")
})

type Lens

type Lens[S, A any] = lens.Lens[S, A]

Lens is an optic that focuses on a field of type A within a structure of type S.

type Operator

type Operator[S, A, B any] = Reader[StateIO[S, A], StateIO[S, B]]

Operator represents a transformation from one StateIO to another. It's a function that takes StateIO[S, A] and returns StateIO[S, B].

Operators are used for:

  • Transforming computations (Map, Chain, etc.)
  • Building reusable computation transformers
  • Composing higher-order operations

func Ap

func Ap[B, S, A any](fa StateIO[S, A]) Operator[S, func(A) B, B]

Ap is the curried version of MonadAp. Returns a function that applies a wrapped function to the given wrapped value.

func ApS

func ApS[ST, S1, S2, T any](
	setter func(T) func(S1) S2,
	fa StateIO[ST, T],
) Operator[ST, S1, S2]

ApS applies a computation in sequence and binds the result to a field. This is the applicative version of Bind, useful for parallel-style composition where computations don't depend on each other's results.

Example:

result := function.Pipe2(
    Do[AppState](Result{}),
    ApS(
        func(count int) func(Result) Result {
            return func(r Result) Result { return Result{count: count} }
        },
        Of[AppState](42),
    ),
)

func Bind

func Bind[ST, S1, S2, T any](
	setter func(T) func(S1) S2,
	f Kleisli[ST, S1, T],
) Operator[ST, S1, S2]

Bind executes a computation and binds its result to a field in the accumulator state. This is used in do-notation to sequence dependent computations.

The setter function takes the computed value and returns a function that updates the accumulator state. The computation function (f) receives the current accumulator state and returns a StateIO computation.

Example:

result := function.Pipe2(
    Do[AppState](Result{}),
    Bind(
        func(name string) func(Result) Result {
            return func(r Result) Result { return Result{name: name, age: r.age} }
        },
        func(r Result) StateIO[AppState, string] {
            return Of[AppState]("John")
        },
    ),
)

func BindTo

func BindTo[ST, S1, T any](
	setter func(T) S1,
) Operator[ST, T, S1]

BindTo wraps a value in a simple constructor, typically used to start a do-notation chain after getting an initial value. This transforms a StateIO[S, T] into StateIO[S, S1] by applying a constructor function.

Example:

result := function.Pipe2(
    Of[AppState](42),
    BindTo[AppState](func(x int) Result { return Result{value: x} }),
)

func Chain

func Chain[S, A, B any](f Kleisli[S, A, B]) Operator[S, A, B]

Chain is the curried version of MonadChain. Returns a function that sequences computations.

Example:

stringify := Chain(func(x int) StateIO[AppState, string] {
    return Of[AppState](fmt.Sprintf("%d", x))
})
result := function.Pipe1(Of[AppState](42), stringify)

func Let

func Let[ST, S1, S2, T any](
	key func(T) func(S1) S2,
	f func(S1) T,
) Operator[ST, S1, S2]

Let computes a derived value and binds it to a field in the accumulator state. Unlike Bind, this does not execute a monadic computation, just a pure function.

The key function takes the computed value and returns a function that updates the accumulator state. The computation function (f) receives the current accumulator state and returns a pure value.

Example:

result := function.Pipe2(
    Do[AppState](Result{age: 25}),
    Let(
        func(isAdult bool) func(Result) Result {
            return func(r Result) Result { return Result{age: r.age, isAdult: isAdult} }
        },
        func(r Result) bool { return r.age >= 18 },
    ),
)

func LetTo

func LetTo[ST, S1, S2, T any](
	key func(T) func(S1) S2,
	b T,
) Operator[ST, S1, S2]

LetTo binds a constant value to a field in the accumulator state. This is useful for setting fixed values in the accumulator during do-notation.

Example:

result := function.Pipe2(
    Do[AppState](Result{}),
    LetTo(
        func(status string) func(Result) Result {
            return func(r Result) Result { return Result{status: status} }
        },
        "active",
    ),
)

func Map

func Map[S, A, B any](f func(A) B) Operator[S, A, B]

Map is the curried version of MonadMap. Returns a function that transforms a StateIO.

Example:

double := Map[AppState](func(x int) int { return x * 2 })
result := function.Pipe1(Of[AppState](21), double)

type Pair

type Pair[L, R any] = pair.Pair[L, R]

Pair represents a tuple of two values.

type Predicate

type Predicate[A any] = predicate.Predicate[A]

Predicate represents a function that tests a value of type A.

type Reader

type Reader[R, A any] = reader.Reader[R, A]

Reader represents a computation that depends on an environment/context of type R and produces a value of type A.

type State

type State[S, A any] = state.State[S, A]

State represents a stateful computation that takes an initial state S and returns a pair of the new state S and a value A.

type StateIO

type StateIO[S, A any] = Reader[S, IO[Pair[S, A]]]

StateIO represents a stateful computation that performs side effects. It combines the State monad with the IO monad, allowing computations that:

  • Manage state of type S
  • Perform side effects (IO)
  • Produce a value of type A

The computation takes an initial state S and returns an IO action that produces a Pair containing the new state S and the result value A.

Type definition: StateIO[S, A] = Reader[S, IO[Pair[S, A]]]

This is useful for:

  • Stateful computations with side effects
  • Managing application state while performing IO operations
  • Composing operations that need both state management and IO

func Do

func Do[ST, A any](
	empty A,
) StateIO[ST, A]

Do starts a do-notation chain for building computations in a fluent style. This is typically used with Bind, Let, and other combinators to compose stateful computations with side effects.

Example:

type Result struct {
    name string
    age  int
}
result := function.Pipe2(
    Do[AppState](Result{}),
    Bind(...),
    Let(...),
)

func FromIO

func FromIO[S, A any](fa IO[A]) StateIO[S, A]

FromIO lifts an IO computation into StateIO. The IO computation is executed and its result is wrapped in StateIO. The state is passed through unchanged.

Example:

ioAction := io.Of(42)
stateIOAction := FromIO[AppState](ioAction)

func MonadAp

func MonadAp[B, S, A any](fab StateIO[S, func(A) B], fa StateIO[S, A]) StateIO[S, B]

MonadAp applies a function wrapped in a StateIO to a value wrapped in a StateIO. The state is threaded through both computations sequentially. This is the applicative apply operation.

Example:

fab := Of[AppState](func(x int) int { return x * 2 })
fa := Of[AppState](21)
result := MonadAp(fab, fa) // Result contains 42

func MonadChain

func MonadChain[S, A, B any](fa StateIO[S, A], f Kleisli[S, A, B]) StateIO[S, B]

MonadChain sequences two computations, passing the result of the first to a function that produces the second computation. This is the monadic bind operation. The state is threaded through both computations sequentially.

Example:

result := MonadChain(
    Of[AppState](5),
    func(x int) StateIO[AppState, string] {
        return Of[AppState](fmt.Sprintf("value: %d", x))
    },
)

func MonadMap

func MonadMap[S, A, B any](fa StateIO[S, A], f func(A) B) StateIO[S, B]

MonadMap transforms the value of a StateIO using the provided function. The state is threaded through the computation unchanged. This is the functor map operation.

Example:

result := MonadMap(
    Of[AppState](21),
    func(x int) int { return x * 2 },
) // Result contains 42

func Of

func Of[S, A any](a A) StateIO[S, A]

Of creates a StateIO that wraps a pure value. The value is wrapped and the state is passed through unchanged.

This is the Pointed/Applicative 'of' operation that lifts a pure value into the StateIO context.

Example:

result := Of[AppState](42)
// Returns a computation containing 42 that passes state through unchanged

type StateIOApplicative

type StateIOApplicative[
	S, A, B any,
] struct{}

StateIOApplicative implements the Applicative typeclass for StateIO. It provides 'Of', 'Map', and 'Ap' operations for applicative composition.

func (*StateIOApplicative[S, A, B]) Ap

func (o *StateIOApplicative[S, A, B]) Ap(fa StateIO[S, A]) Operator[S, func(A) B, B]

Ap applies a function wrapped in StateIO to a value wrapped in StateIO.

func (*StateIOApplicative[S, A, B]) Map

func (o *StateIOApplicative[S, A, B]) Map(f func(A) B) Operator[S, A, B]

Map transforms the value within a StateIO using the provided function.

func (*StateIOApplicative[S, A, B]) Of

func (o *StateIOApplicative[S, A, B]) Of(a A) StateIO[S, A]

Of lifts a pure value into the StateIO context.

type StateIOFunctor

type StateIOFunctor[
	S, A, B any,
] struct{}

StateIOFunctor implements the Functor typeclass for StateIO. It provides the 'Map' operation to transform values within the StateIO context.

func (*StateIOFunctor[S, A, B]) Map

func (o *StateIOFunctor[S, A, B]) Map(f func(A) B) Operator[S, A, B]

Map transforms the value within a StateIO using the provided function.

type StateIOMonad

type StateIOMonad[
	S, A, B any,
] struct{}

StateIOMonad implements the Monad typeclass for StateIO. It provides 'Of', 'Map', 'Chain', and 'Ap' operations for monadic composition.

func (*StateIOMonad[S, A, B]) Ap

func (o *StateIOMonad[S, A, B]) Ap(fa StateIO[S, A]) Operator[S, func(A) B, B]

Ap applies a function wrapped in StateIO to a value wrapped in StateIO.

func (*StateIOMonad[S, A, B]) Chain

func (o *StateIOMonad[S, A, B]) Chain(f Kleisli[S, A, B]) Operator[S, A, B]

Chain sequences two StateIO computations, threading state through both.

func (*StateIOMonad[S, A, B]) Map

func (o *StateIOMonad[S, A, B]) Map(f func(A) B) Operator[S, A, B]

Map transforms the value within a StateIO using the provided function.

func (*StateIOMonad[S, A, B]) Of

func (o *StateIOMonad[S, A, B]) Of(a A) StateIO[S, A]

Of lifts a pure value into the StateIO context.

type StateIOPointed

type StateIOPointed[
	S, A any,
] struct{}

StateIOPointed implements the Pointed typeclass for StateIO. It provides the 'Of' operation to lift pure values into the StateIO context.

func (*StateIOPointed[S, A]) Of

func (o *StateIOPointed[S, A]) Of(a A) StateIO[S, A]

Of lifts a pure value into the StateIO context.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL