A functional programming language focused around maps. https://github.com/PolyglotSymposium/mappy
|Latest on Hackage:||0.1.3.1|
This package is not currently in any snapshots. If you're interested in using it, we recommend adding it to Stackage Nightly. Doing so will make builds more reliable, and allow stackage.org to host generated Haddocks.
A functional programming language. Like LISP but focused around maps rather than lists.
To install, use cabal
cabal install mappy
Installing from source
To build install from source, use stack
this will install the
mappy executable. The exact location may vary, on my
machine, this installs to
mappy has a REPL that is activated by running mappy with no arguments.
Running a mappy program
To run a mappy program, simple run the executable, giving it the path of a
mappy source file (ensure it has a
main function defined). For example, to
run the prelude, run
main = [give :print "Hello, World" io]
For less contrived examples, see the prelude.
Keywords are names in mappy that always evaluate to themselves. Keywords begin
: and can have a wide range of values, for example, the following are
Keywords are primarily useful for naming things, like keys in a map.
The primary value in mappy is the map (a la Hash in Ruby, HashMap in Java, etc...). To define a map, surround key value pairs with parenthesis.
The empty map
A map containing maps
( :type :person, :job ( :title :hacker-pro, :salary :infinity ) )
Note that, like in Clojure, commas are parsed as whitespace in mappy.
Lists are really just a special form of maps. Because of this, there's
another sugar to handle them using the
|) delimiters. For, example
here's a list of some keywords
(|:a :b :c :d :e|)
Characters are a special form of maps (noticing a pattern here?). As in other languages, characters are surrounded by single quotes
"I am a nice string!\nHave a good day :)"
For less contrived examples, see the prelude.
mappy doesn't really have variables. Instead, you can bind names to values
answer = :forty-two
this binds the name
answer to the keyword
You can also define functions that operate on values
first a b = a
this creates a function named
first that takes two arguments and returns the
Functions can have lazy arguments
mappy has ML-esque let expressions. For example, here's how
filter p? xs = [ if [empty? xs] nil let first = [take :head xs] rest = [take :tail xs] in [if [p? first] [cons first [filter p? rest]] [filter p? rest] ] ]
Note that let expressions are just syntactic sugar over nested lambdas.
To apply functions, use square brackets, e.g.
the-first-value = [first :a :b]
this applies the
first function, defined above, to
:b and binds
the-first-value to the result.
In mappy, functions are automatically partially applied if too few arguments are given. This feature, is well aligned with functional programming because it allows us to easily build new functions from existing ones.
For example, suppose we wanted to build a function that adds two to a number. We might do so like thus
add-two num = [add two num]
This is pretty nice, but in mappy it can get more elegantly
add-two = [add two]
In mappy, IO is done using the special
io map, using the core primitives.
To print a value, use
[give :print "Hi, from mappy!" io]
Writing a file
To write a file, use
[give :write-file (:text "File content", :file "out.txt") io]
Reading a file
To read a file, use
[take (:read-file "README.md") io]
To create a lambda function the syntax
\arg1 arg2 argN -> body is used, where
argX are the argument names and
body is an expression. So, if we wanted to
first function above, using lambdas, it would look like
first = \a b -> a
Note that lambdas can have "lazy-arguments" (by wrapping in parenthesis), for
example, here's how
if is defined in mappy
if cond (then) (else) = [[ default-take [take :truthy cond] (:false else) then ]]
]] are not special, they are normal function application. In
this case, they apply
default-take then the result (either
Like LISP, mappy is built around a small set of core primitives. mappy's core
primitives are called
default-take. Being as mappy is
built around maps, all core primitives operate on maps.
Returns a new map with a new association. If the given key existed in the map before, then it will be overwritten in the returned copy.
[give :foo :bar (:baz :quux)]
(:baz :quux :foo :bar)
Attempts to retrieve a value from a map. If the value is not present, an error will occur.
[take :foo (:foo :bar)]
More simply, applying a keyword to a map is the same as applying
example, the above example can be rewritten as
[:foo (:foo :bar)]
take except, instead of erring, it returns a default value if the key is
[default-take :foo (:baz :quux) :bar]