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