Navigation

Sugar

Opt-in syntax-livability layer for nix-effects.

  • fx.sugar.do / fx.sugar.letM — combinator forms
  • fx.sugar.operators.__div/ as reverse-apply (bind)
  • re-exports of pure bind run handle map seq pipe kleisli

See book/src/sugar.md for the opt-in matrix and caveats.

bind

Re-export of fx.kernel.bind. See fx.kernel for details.

do

do: build a composable Kleisli arrow (a -> M b) from a list of steps; plain functions (a -> b) are auto-lifted via pure, monadic functions (a -> M b) pass through. Apply the result to a seed to obtain a Comp b.

do : [a -> b | a -> M b] -> (a -> M b)

Use when steps form a linear pipeline that should remain point-free and composable. Because do returns a Kleisli arrow, two pipelines glue without lambdas: kleisli (do [f g]) (do [h i]) == do [f g h i]. Data-last argument order makes map (do [validate enrich]) xs natural.

Auto-lifting lets pure and effectful steps mix freely — runtime dispatch via isComp decides whether to thread through map or bind. An empty list gives the identity arrow x: pure x.

Prefer steps when discarding intermediate values is intentional (effect sequencing). Prefer kleisli for a single binary composition. Use letM when bound values are siblings rather than a left-to-right pipeline.

handle

Re-export of fx.trampoline.handle. See fx.trampoline for details.

kleisli

Re-export of fx.kernel.kleisli. See fx.kernel for details.

letM

letM: attrs-based monadic binding — runs bindAttrs attrs to gather a record of results, then passes the record to continuation k for the next step.

letM : { name = Comp a } -> ({ name = a } -> Comp b) -> Comp b

Use when several independent computations must complete before a single dependent step runs — letM { a = ca; b = cb; } ({ a, b }: ...) replaces a nested bind ca (a: bind cb (b: ...)) chain with a flat record. Field order in the resulting record is unspecified; ordering of side effects across fields is determined by bindAttrs, not by the caller's attribute layout.

Prefer over do when the bound values are siblings rather than a left-to-right pipeline. For a single bind, plain bind is clearer.

map

Re-export of fx.kernel.map. See fx.kernel for details.

operators

operators: sugar operator overloads — currently __div aliasing fx.kernel.bind so c / k reads as bind c k in expression context.

Opt-in operator-style monadic composition. The attrset's __div field is invoked by Nix's / operator when the LHS is an attrset carrying __div. Use sparingly: operator overloading is non-idiomatic in Nix and obscures dataflow for readers unfamiliar with the convention. The combinator forms (bind, do, kleisli) remain the canonical entry points.

pipe

Re-export of fx.kernel.pipe. See fx.kernel for details.

pure

Re-export of fx.kernel.pure. See fx.kernel for details.

run

Re-export of fx.trampoline.run. See fx.trampoline for details.

seq

Re-export of fx.kernel.seq. See fx.kernel for details.

steps

steps: sequence a list of Comp steps left-to-right via bind, returning a single composed Comp; each step receives the previous result, the seed is pure null.

steps : [a -> Comp b] -> Comp b

Use when only the side effects of each step matter — analogous to Haskell's sequence_. The seed value is pure null, so the first step's argument is null; discard it if the first step is producer-shaped (_: pure x). An empty list returns pure null.

Prefer do for composable, point-free Kleisli pipelines that thread values. Prefer kleisli when composing two Kleisli arrows without an initial value, and pipe when threading a non-monadic seed through effectful transforms.

types

types: refinement-applicable type wrappers — each primitive becomes a __functor-bearing record so Int (n: n > 0) chains another refined layer; wrap lifts an arbitrary type into the same callable form.

Sugar layer over fx.types.refinement.refined. Each entry carries __functor self pred = sugared (refined "name?" self pred), so calling a type as a function tightens it with a predicate. __toString self = self.name makes interpolated names readable. The wrapped primitives (Int, String, Bool, Float, Path, Null, Unit, Any) come from fx.types.primitives; wrap lets callers lift any other type into the same chainable shape.

Source