Giter Site home page Giter Site logo

polyglotsymposium / mappy Goto Github PK

View Code? Open in Web Editor NEW
12.0 5.0 1.0 172 KB

A functional programming language. Like LISP but focused around maps rather than lists.

License: BSD 3-Clause "New" or "Revised" License

Haskell 100.00%
functional-programming programming-language haskell haskell-library mappy lisp interpreter repl

mappy's Introduction

mappy

Build Status Hackage Actively Maintained

A functional programming language. Like LISP but focused around maps rather than lists.

Installing

To install, use cabal

cabal install mappy

Installing from source

To build install from source, use stack

stack install

this will install the mappy executable. The exact location may vary, on my machine, this installs to ~/.local/bin.

REPL

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

mappy prelude/prelude.map

Hello, world

main = [give :print "Hello, World" io]

Values

For less contrived examples, see the prelude.

keywords

Keywords are names in mappy that always evaluate to themselves. Keywords begin with : and can have a wide range of values, for example, the following are keywords

  • :i-am-a-keyword
  • :----->
  • :KeywordHmmmm
  • :/

Keywords are primarily useful for naming things, like keys in a map.

Maps

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.

Examples

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

Lists are really just a special form of maps. Because of this, there's another sugar to handle them using the (| and |) delimiters. For, example here's a list of some keywords

(|:a :b :c :d :e|)

Characters

Characters are a special form of maps (noticing a pattern here?). As in other languages, characters are surrounded by single quotes

'm'

Strings

Strings are lists of characters. As in other languages, they are surrounded by double quotes

"I am a nice string!\nHave a good day :)"

Grammar

For less contrived examples, see the prelude.

Binding values

mappy doesn't really have variables. Instead, you can bind names to values

answer = :forty-two

this binds the name answer to the keyword :forty-two.

Binding functions

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 first one.

Functions can have lazy arguments

Let expressions

mappy has ML-esque let expressions. For example, here's how filter is defined

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.

Applying functions

To apply functions, use square brackets, e.g.

the-first-value = [first :a :b]

this applies the first function, defined above, to :a and :b and binds the name the-first-value to the result.

Partial application

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]

IO

In mappy, IO is done using the special io map, using the core primitives.

Printing

To print a value, use give

[give :print "Hi, from mappy!" io]

Writing a file

To write a file, use give

[give :write-file (:text "File content", :file "out.txt") io]

Reading a file

To read a file, use take

[take (:read-file "README.md") io]

Lambda functions

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 define our first function above, using lambdas, it would look like

first = \a b -> a

Lazy arguments

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
]]

the [[ and ]] are not special, they are normal function application. In this case, they apply default-take then the result (either else or then).

Core primitives

Like LISP, mappy is built around a small set of core primitives. mappy's core primitives are called give, take and default-take. Being as mappy is built around maps, all core primitives operate on maps.

give

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.

Example:

[give :foo :bar (:baz :quux)]

returns: (:baz :quux :foo :bar)

take

Attempts to retrieve a value from a map. If the value is not present, an error will occur.

Example:

[take :foo (:foo :bar)]

returns: :bar

More simply, applying a keyword to a map is the same as applying take. For example, the above example can be rewritten as

[:foo (:foo :bar)]

default-take

Like take except, instead of erring, it returns a default value if the key is not found.

Example:

[default-take :foo (:baz :quux) :bar]

returns: :bar

mappy's People

Contributors

mjgpy3 avatar pthariensflame avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

mappy's Issues

Bring specs up to date

With the strict addition (for fixing infinite recursion) many new tests were ignored.

Proper "runner"

Currently, the runner spits out very Haskelly things. It should probably pretty print its output.

Fix `add` example

In prelude.map, there's a nice add function. It works for trivial inputs (where the first value is zero ()) but anything else recurses infinitely.

Lisp Backend

This is probably even more doable than #4. Macros could easily represent functions with thunked parameters.

String pretty printer

Depends on #24. Once strings are done, the pretty printer should recognize them and print them nicely.

Char pretty printer

Depends on #32. Basically, once the sugar is done, it would be nice for the pretty printer to recognize chars and print them as such.

`take` sugar

It would be nice if

[:foo some-map]

desugared to

[take :foo some-map]

Fix cabal Repl issue

For some reason, when trying to install, I get

app/Main.hs:4:8:
    Could not find module ‘Repl’
    Use -v to see a list of the files searched for.
Failed to install mappy-0.1.0.0
cabal: Error: some packages failed to install:
mappy-0.1.0.0 failed during the building phase. The exception was:
ExitFailure 1

The repl must not be exposed correctly...

Vim highlighting

Would be awesome! I'll bet the clojure vim plugin can be modified to make this (they're kind of similar).

Add print

a la Haskell's trace would probably fit best with the language's expression semantics

Fix nasty scoping error

In the repl

m> [identity :foo]
:foo
m> [identity]
:foo

The previous environment seems to be hanging around.

Implement `in-env`

This will be similar to let see #14, however, it will be a simple map providing a lookup function.

For example:

[in-env (:a :b :c :d)
  \env -> [env :a]]

Would result in :b

Sugar for fn def

Make a sugar for function definition like, a la clojure's defn.

Basically:

myfn a b c = body

would become:

myfn = \a b c -> body

Consider Strict maps

I'm not really sure how Data.Map differs from Data.Map.Strict. It would be nice to figure out which is more appropriate for mappy

Commas are whitespace

Clojure does this, I think it would be useful with the language's focus on single expressions

Eval errors should accumulate

Looking at the code, it looks like evaluating arguments happens as a pipline. E.g.

[default-take dont-exist1 dont-exist2 dont-exist3]

Will error with

[NameNotDefined "dont-exist1"]

It would be nice if it errored with

[NameNotDefined "dont-exist1", NameNotDefined "dont-exist2", NameNotDefined "dont-exist3"]

Not just for default-take but for any primitive function, as well as custom functions/lambdas. The latter may already work this way, I'll have to check.

Maps don't like newlines

Running:

foo = (
  :a :b
)

Results in:

unexpected "\n"
expecting "(", "[", "", ":", letter, digit or ")"

Fix missed warnings

For some odd reason, -Wall didn't pick up all files. There's still some work to be done, resolving warnings.

Fix first class functions

They don't seem to work:

identity = \a -> a

apply = \fn v -> [fn v]

main = [apply identity :bar]

results in

Left [NameNotDefined "v"]

Use HLint

This would be nice for code style

Error printer

Currently the runner just prints the Haskell data structures for errors (not very useful). An error pretty printer would be nice.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.