MyNixOS website logo
Description

Template Haskell library for writing monadic expressions more easily.

See README at the bottom.

Getting started: See Each.

each

Inspired by the Scala library of the same name, each is a Template Haskell library that transforms expressions containing invocations of impure subexpressions into do-notation. Just mark your impure subexpressions with bind or ~! and they will be called appropriately, as in this small demo:

ghci> :m Each
ghci> $(each [| "Hello, " ++ (~! getLine) |])
World              <--[keyboard input]
"Hello, World"

With the ApplicativeDo GHC extension, calls to fmap and <*> will be arranged so that you don't need to worry if you use, say, Haxl and needs Applicative for parallelism.

Most constructs where this would make things much more simpler are already supported. In particular, these are okay:

  • Nested binds.
  • Branching constructs, even if the branches themselves uses bind. The generated do-notation will generally match imperative intuition.

These are some quirks:

  • let expressions are evaluated sequentially. each currently lacks support for detecting pure let expressions.

  • where is not implemented.

  • Parameters to lambda functions may not be used impurely. This is acceptable, but the error message may be confusing:

      ghci> :m Each
      ghci> $(each [| (\x -> bind x) |])
    
      <interactive>:25:3: error:
      • The exact Name ‘x_acBv’ is not in scope
      Probable cause: you used a unique Template Haskell name (NameU),
      perhaps via newName, but did not bind it
      If that's it, then -ddump-splices might be useful
      • In the untyped splice: $(each [| (\ x -> bind x) |])
    

    Also, binds in the lambda will be run when the lambda is constructed, not when it's called.

  • PatternGuard, LambdaCase and a few other extensions (uncertain) are not yet implemented.

If you find something wrong, or really want some feature, feel free to leave an issue.

How it works

The basic structure of an each block is this:

$(each [| ... |])

Inside of this block, three (interchangable) ways are used to mark impure subexpressions:

  • bind expr
  • bind $ expr
  • (~! expr)

do-notation is generated according to left-to-right order, and branching is handled.

More demos

A more detailed demo:

ghci> :m Each
ghci> :{
    | $(each [|
    |   "Hey it works"
    |   ++ show (length $
    |     "something"
    |     ++ (~! readFile "/etc/issue")
    |     ++ (~! readFile "/etc/issue.net"))
    | |])
    | :}
"Hey it works64"

Nested binds also work as expected.

ghci> :m Each
ghci> prompt str = putStrLn str *> getLine
ghci> $(each [| "Nah just " ++ (~! prompt ("What's " ++ bind getLine ++ "?")) |])
something          <--[keyboard input]
What's something?
nothing            <--[keyboard input]
"Nah just nothing"
Metadata

Version

1.1.1.0

Platforms (75)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    WASI
    Windows
Show all
  • aarch64-darwin
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64_be-none
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-darwin
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-darwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • vc4-none
  • wasm32-wasi
  • wasm64-wasi
  • x86_64-cygwin
  • x86_64-darwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-windows