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