Functional
Hush provides first class higher order functions, which is the fundamental building block for functional programming. As such, the paradigm integrates very well with the language, and it is recommend as the main approach for solving problems in Hush.
Here’s an example of a simple iterator library, using functional programming and implementing basic laziness:
# Iterator: traversal utilities.
# Construct an iterator.
# An Iterator is an object capable of successively yielding values.
# Iterators are *mutable* (they may mutate their inner state as they iterate).
# Parameters:
# * next: function() -> @[ finished: bool, value: any ] # the iterator function
# Returns the Iterator instance.
function Iterator(next)
@[
# Provide the next method.
next: next,
# Get the iterator function.
# Iterating mutates the iterator, which will successively yield the next value.
# This should be used in for loops, as in:
# for item in iterator.iter() do
# ...
# end
iter: function ()
self.next
end,
# Get the nth element in the iterator.
# This is equivalent to calling self.next() `n + 1` times,
# but might be optimized for some iterators.
# Parameters:
# * n: the desired element index
# Returns the nth element.
nth: function (n)
for _ in std.range(0, n, 1) do
let iteration = self.next()
if iteration.finished then
return iteration
end
end
self.next()
end,
# Map a function over an iterator.
# This function is lazy.
# Parameters:
# * fun: function(any) -> any # the function to apply
# Returns a new iterator, consuming self.
map: function (fun)
let map = function (iteration)
if iteration.finished then
iteration
else
@[ finished: false, value: fun(iteration.value) ]
end
end
let base = self
let it = Iterator(
function ()
map(base.next())
end
)
# Elide mapping unnecessary elements when jumping.
# This is an important optimization for, e.g.:
# Array([1,2,3,4,5])
# .map(fun)
# .skip(3)
# .collect()
it.nth = function (n)
map(base.nth(n))
end
it
end,
# Filter the iterator by the given predicate.
# This function is lazy.
# Items for which the predicate returns true are kept.
# Parameters:
# * pred: function(any) -> bool # the predicate
# Returns a new iterator, consuming self.
filter: function (pred)
let base = self
Iterator(
function ()
let iteration = base.next()
while not (iteration.finished or pred(iteration.value)) do
iteration = base.next()
end
iteration
end
)
end,
# Skip elements from the iterator.
# Equivalent to calling `nth(size - 1)` on an iterator.
# Returns a new iterator, consuming self.
skip: function(size)
if size > 0 then
self.nth(size - 1)
end
self
end,
# Iterate up to some elements.
# This function is lazy.
# The iterator that will stop after `size` elements.
# Parameters:
# * size: int # how many elements to keep
# Returns a new iterator, consuming self.
take: function (size)
let base = self
let it = Iterator(
function ()
if size == 0 then
@[ finished: true ]
else
size = size - 1
base.next()
end
end
)
it.nth = function(n)
if n + 1 > size then
size = 0
@[ finished: true ]
else
size = size - n
base.nth(n)
end
end
it
end,
# Fold elements into a single value.
# Parameters:
# * merge: function(any, any) -> any # the function to merge elements
# * acc: any # the default argument for merge
# Returns the resulting any, consuming self.
fold: function (merge, acc)
for item in self.iter() do
acc = merge(item, acc)
end
acc
end,
# Collect the iterator's items.
# Parameters:
# * target: nil, array or function(any) # where to collect
# If target is nil, the elements are collected into a new array.
# If target is an array, the elements are collected by pushing to it.
# If target is a function, the elements are collected by calling the function.
# Returns nil if target is a function, or the resulting array otherwise.
collect: function (target)
let result = []
let push
let type = std.type(target)
if type == "function" then
result = nil
push = target
else
if type == "array" then
result = target
end
push = function (item)
std.push(result, item)
end
end
for item in self.iter() do
push(item)
end
result
end
]
end
# Construct an iterator for the given array.
# Returns the Iterator instance.
function Array(array)
let ix = 0
# Don't use `std.iter` in order to be able to optimize `nth`.
let it = Iterator(
function ()
if ix >= std.len(array) then
@[ finished: true ]
else
let value = array[ix]
ix = ix + 1
@[ finished: false, value: value ]
end
end
)
# Override nth with a O(1) implementation:
it.nth = function (n)
ix = ix + n
return it.next()
end
return it
end
# Construct an iterator for the given table.
# There is no guarantee of iteration order.
# Returns the Iterator instance.
function Dict(dict)
return Iterator(std.iter(dict))
end
# Construct an empty iterator.
# Returns the Iterator instance.
function Empty()
return Iterator(
function ()
return @[ finished: true ]
end
)
end