Skip to content

Functional Programming in Harneet

Harneet is Haskell-inspired, not Haskell-like: it aims to offer practical, type-safe functional patterns without the steep learning curve. You get first-class functions, higher-order functions, closures, and composition patterns while keeping imperative ergonomics.

  • "Haskell-inspired" rather than "Haskell-like"
  • "Functionally powerful" rather than "purely functional"
  • "Type-safe functional" rather than "academic functional"

This guide shows composition, piping, map/filter/reduce, and a simple chaining pipeline you can copy into your projects.

Composition

Use typed helpers for predictable, type-checked composition.

Composition Helpers
package main
import fmt

// composeInt(f, g): returns x -> f(g(x)) where f,g: int->int
function composeInt(f function(int) int, g function(int) int) function(int) int {
    return function(x int) int { return f(g(x)) }
}

// composeStrInt(f, g): returns x -> f(g(x)) where g: int->int, f: int->string
function composeStrInt(f function(int) string, g function(int) int) function(int) string {
    return function(x int) string { return f(g(x)) }
}

function inc(n int) int { return n + 1 }
function double(n int) int { return n * 2 }
function toString(n int) string { return fmt.Sprintf("%d", n) }

var incAfterDouble = composeInt(inc, double)
fmt.Printf("composeInt(inc, double)(3) = %d\n", incAfterDouble(3))  // 7

var toStrAfterIncAfterDouble = composeStrInt(toString, composeInt(inc, double))
fmt.Printf("composeStrInt(toString, composeInt(inc, double))(5) = %s\n", toStrAfterIncAfterDouble(5))

Piping

Left-to-right application with a typed helper. This example uses int -> int functions.

Piping Helper
// pipeInt(x, f1, f2, ...) applies int->int functions in order
package main
import fmt
function pipeInt(x int, fns array) int {
    var result = x
    for fn in fns { result = fn(result) }
    return result
}

function inc(n int) int { return n + 1 }
function double(n int) int { return n * 2 }

fmt.Printf("pipeInt(3, [double, inc]) = %d\n", pipeInt(3, [double, inc]))  // 7

Map / Filter / Reduce (typed helpers)

Use typed helpers for int arrays. These examples rely on the standard arrays module for safe appends.

Typed Map/Filter/Reduce
package main
import fmt
import arrays

function mapInt(arr array, f function(int) int) array {
    var out = []
    for x in arr {
        var next, err = arrays.push(out, f(x))
        if err == None { out = next }
    }
    return out
}

function filterInt(arr array, p function(int) bool) array {
    var out = []
    for x in arr {
        if p(x) {
            var next, err = arrays.push(out, x)
            if err == None { out = next }
        }
    }
    return out
}

function reduceInt(arr array, init int, f function(int, int) int) int {
    var acc = init
    for x in arr { acc = f(acc, x) }
    return acc
}

function inc(n int) int { return n + 1 }
function isEven(n int) bool { return n % 2 == 0 }
function sum(a int, b int) int { return a + b }

var nums = [1, 2, 3, 4, 5]
var mapped = mapInt(nums, inc)          // [2,3,4,5,6]
var evens  = filterInt(mapped, isEven)  // [2,4,6]
var total  = reduceInt(evens, 0, sum)   // 12

fmt.Printf("mapped: %s\n", mapped)
fmt.Printf("evens: %s\n", evens)
fmt.Printf("sum: %d\n", total)

Chaining Pipeline

A deterministic pipeline (map -> filter -> reduce) without anonymous inline lambdas (works well with current parser).

Chaining Pipeline
package main
import fmt
import arrays

function composeInt(f function(int) int, g function(int) int) function(int) int {
    return function(x int) int { return f(g(x)) }
}

function mapInt(arr array, f function(int) int) array {
    var out = []
    for x in arr {
        var next, err = arrays.push(out, f(x))
        if err == None { out = next }
    }
    return out
}

function filterInt(arr array, p function(int) bool) array {
    var out = []
    for x in arr {
        if p(x) {
            var next, err = arrays.push(out, x)
            if err == None { out = next }
        }
    }
    return out
}

function reduceInt(arr array, init int, f function(int, int) int) int {
    var acc = init
    for x in arr { acc = f(acc, x) }
    return acc
}

function inc(n int) int { return n + 1 }
function square(n int) int { return n * n }
function isOdd(n int) bool { return n % 2 == 1 }
function add(a int, b int) int { return a + b }

var data = [1,2,3,4,5]
var squareAfterInc = composeInt(square, inc)

var mapped = mapInt(data, squareAfterInc)
var filtered = filterInt(mapped, isOdd)
var result = reduceInt(filtered, 0, add)

fmt.Printf("result = %d\n", result)

Tips

  • Prefer typed helpers over any when composing functions; this works best with the current type checker.
  • Use arrays.push rather than append—Harneet’s stdlib provides an array module with safe operations and error tuples.
  • If you want compose/pipe that work across multiple types, create additional typed variants (e.g., composeStrInt, composeFloat, etc.).

See Also

  • Examples in repo:
  • examples/functional/compose.ha
  • examples/functional/map_filter_reduce.ha
  • examples/functional/chaining_demo.ha
  • Standard library: site/docs/stdlib/arrays.md
  • Functions overview: site/docs/functions/introduction.md