Skip to content

Search content

From F# to Gleam > 3: Functions, Pipelines, and Partial Application

3 min readFrom F# to GleamPart 3
A string flows through trim, lowercase, and replace functions, with an add function capture below.

Gleam has first-class functions and a |> operator. That’s enough to make an F# pipeline look comfortable, but there’s one important trap: argument order.

Table of contents

On this page

Currying Isn’t Automatic

In F#, supplying one argument can produce another function:

let add amount value = amount + value
let addTen = add 10

assert (addTen 5 = 15)scratch.fsx

Gleam’s add(10) would be a call with a missing argument, not automatic partial application. Use a closure or a function capture instead:

import gleam/string

fn add(amount: Int, value: Int) -> Int {
  amount + value
}src/learning_gleam.gleam

Inside a function, let add_ten = add(10, _) creates a one-argument function. It’s shorthand for fn(value) { add(10, value) }. A capture has one placeholder. Use an explicit fn when the transformation needs more than that.

Same Pipe, Different Insertion Point

Consider replacing spaces in a label:

let replaceSpaces (text: string) = text.Replace(" ", "-")

let slug =
    "  Gleam Language  ".Trim().ToLowerInvariant()
    |> replaceSpaces

assert (slug = "gleam-language")scratch.fsx

Gleam’s standard library places the string before the replacement arguments:

// shortcut: replaces spaces only, use a slug library for public URLs.
fn slug(text: String) -> String {
  text
  |> string.trim
  |> string.lowercase
  |> string.replace(" ", "-")
}src/learning_gleam.gleam

The last line means string.replace(previous_value, " ", "-"). More generally, value |> function(a, b) becomes function(value, a, b).

In F#, value |> function a b means function a b value for an ordinary curried function. Don’t mechanically preserve argument order when translating a helper.

If the subject really belongs elsewhere, a capture can put it there: 5 |> add(10, _) means add(10, 5).

Generic Functions Still Stay Generic

Gleam uses lowercase type variables where F# writes 'a. Neither means an unchecked dynamic value:

let twice transform value = transform (transform value)

assert (twice ((+) 1) 10 = 12)
assert (twice (fun text -> text + "!") "hello" = "hello!!")scratch.fsx
fn twice(value: a, transform: fn(a) -> a) -> a {
  transform(transform(value))
}src/learning_gleam.gleam

For each call, the input, output, and callback must agree on one type. The function can’t silently turn an Int into a String halfway through.

Try It

Replace src/learning_gleam.gleam with the Gleam blocks above and this main, then run gleam run:

pub fn main() -> Nil {
  let add_ten = add(10, _)
  let exclaim = fn(text) { text <> "!" }

  assert add_ten(5) == 15
  assert { 5 |> add(10, _) } == 15
  assert slug("  Gleam Language  ") == "gleam-language"
  assert slug("") == ""
  assert twice(10, add(1, _)) == 12
  assert twice("hello", exclaim) == "hello!!"
}src/learning_gleam.gleam

The slug function is deliberately only a spaces-to-hyphens example, not a general URL slug generator. Its useful lesson is how the data flows, not how to normalise every possible input.

Further reading: pipelines and function captures.



Previous in series
From F# to Gleam > 2: Records, Custom Types, and Pattern Matching
Next in series
From F# to Gleam > 4: Lists, Dictionaries, and Eager Transformations