Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@ympbyc
Last active December 19, 2015 10:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ympbyc/5938797 to your computer and use it in GitHub Desktop.
Save ympbyc/5938797 to your computer and use it in GitHub Desktop.
FP with message-passing syntax
======= INPUT ======

VEC `map` FN =
  (VEC rest `map` FN) conj: (VEC first FN)


FOO goodNumber? =
  FOO `<` 5 i: true f: false


BAR =
  [X 1, Y 2] let: X `*` Y


N snakeOfAge =
  { :name :sam :age N }


SNAKE birthday =
  SNAKE as: :age soc: (SNAKE `get` :age) `+` 1


APP main: ARGS =
  [1 2 3 4.89 5] `map` sqrt `map` snakeOfAge `map` birthday



======= OUTPUT ======

((clojure.core/defn
  map
  [VEC FN]
  (conj (map (rest VEC) FN) (FN (first VEC))))
 (clojure.core/defn goodNumber? [FOO] (if (< FOO 5) true false))
 (def BAR (let [X 1 Y 2] (* X Y)))
 (clojure.core/defn snakeOfAge [N] {:age N, :name :sam})
 (clojure.core/defn
  birthday
  [SNAKE]
  (assoc SNAKE :age (+ (get SNAKE :age) 1)))
 (clojure.core/defn
  main
  [APP ARGS]
  (map (map (map [1 2 3 4.89 5] sqrt) snakeOfAge) birthday)))
map :: [a] * (a -> b) -> [b]
VEC map: FN =
  (FN value: VEC fst) consOnto: (VEC rest map: FN)

Primitives

keywords

:xx :y :zazaza

Numbers

1 2 3

Strings

"hello" "hi"

Compound Types

Persistent Vectors

[1 2 3 4] ['a' 'b' 'c']

Persistent Maps

{:x 5 :y 'hi' [1] 7}

Lambdas -- Unavailable (Yet)

Message Passing

One thing I like about Lisp, Smalltalk and concatenative languages is that they do not have implicit priorities in application of procedures. Smalltalk's unary, binary and keyword messages varying in priority is a good balance I think.

Unary Message (highest priority)

{:age 5 :name "Sam"} birthday

Binary Message (second highest in priority)

2 + 3

Keyword Message (lowest priority)

{:x 1 :y 2} assoc: :x with: 8

Function Definition

Definition

VEC map: FN =
  (VEC fst FN) consOnto: (VEC rest map: FN)

Type System

Type system should not be that decent. Primitive types and aliasing should be enough. Letting users to form a hierarchy of types is undesirable. Type inference is essential.

Aliasing

snake :: {:age number :name string}

Function Typing

birthday :: snake -> snake
SNAKE birthday =
  SNAKE assoc: :age with: (SNAKE 's :age + 1)

Implentation Detail

(ns smallfp.smallparser
(:require [instaparse.core :as insta]))
(def parser
(insta/parser "
<program> = <OSP> def (<NEWLINE>+ def)* <OSP>
<expr> = token | wrappedMessage | keywordMessage | binaryMessage | unaryMessage | compounddata
<strongExpr> = token | wrappedMessage | binaryMessage | unaryMessage | compounddata
<strongestExpr> = token | wrappedMessage | unaryMessage | compounddata
<token> = string | keyword | parameter | symbol | number
<message> = keywordMessage | binaryMessage | unaryMessage
<wrappedMessage> = <OPENPAREN> message <CLOSEPAREN>
keywordMessage = (strongExpr (<SP> kselector <SP> strongExpr)+)
binaryMessage = strongExpr <SP> bselector <SP> strongestExpr
unaryMessage =(expr <SP> (uselector | parameter))
<def> = definition | defconst
definition = message <SP> <EQUAL> <SP> expr <OSP> <END>
defconst = parameter <SP> <EQUAL> <SP> expr <OSP> <END>
<compounddata> = vector | map
vector = <'['> <OSP> expr? (<SEP> expr)* <OSP> <']'>
map = <'{'> <OSP> (mapkey <SP> mapvalue)? (<SEP> mapkey <SP> mapvalue)* <OSP> <'}'>
<mapkey> = expr
<mapvalue> = expr
kselector = ALPHANUMANIC <COLON>
bselector = <'`'> ALPHANUMANIC <'`'>
uselector = symbol
string = #'\"(.*)*\"'
keyword = <COLON> ALPHANUMANIC
number = (digit+) | (digit+ DECIMALP digit+)
symbol = ALPHA ALPHANUMERIC*
parameter = CAPITALPHA ALPHANUMERIC*
<digit> = #'[0-9]'
<ALPHA> = #'[a-z]'
<CAPITALPHA> = #'[A-Z]'
<ALPHANUMERIC> = #'[a-zA-Z0-9_?!$-+*<>]'
<ALPHANUMANIC> = ALPHANUMERIC+
<COLON> = ':'
OPENPAREN = '('
CLOSEPAREN = ')'
EQUAL = '='
SP = #'\\s+'
OSP = SP?
SEP = <OSP> <','> <OSP> | SP
NEWLINE = #'\n'
END = (NEWLINE NEWLINE | 'end')
<DECIMALP> = '.'
"))
(defn unary-message-transformer
[receiver selector]
`(~selector ~receiver))
(defn keyword-message-transformer
[receiver & mes]
(let [pairs (apply array-map mes)
fnname (->> (keys pairs)
(clojure.string/join "")
symbol)]
(cons fnname (cons receiver (vals pairs)))))
(defn binary-message-transformer
[receiver op arg]
(list op receiver arg))
(defn map-transformer
[& kvs]
(apply hash-map kvs))
(defn definition-transfoter
[[form & args] expr]
`(defn ~form [~@args] ~expr))
(defn defconst-transformer
[param expr]
`(def ~param ~expr))
(defn smallparser [x]
(println "======= INPUT ======")
(println x)
(println "======= OUTPUT ======
")
(insta/transform
{:string read-string
:symbol `~(comp symbol str)
:keyword (comp keyword str)
:parameter (comp symbol str)
:number (comp read-string str)
:kselector (comp symbol str)
:bselector (comp symbol str)
:uselector (comp symbol str)
:unaryMessage unary-message-transformer
:keywordMessage keyword-message-transformer
:binaryMessage binary-message-transformer
:map map-transformer
:vector vector
:definition definition-transfoter
:defconst defconst-transformer}
(parser x)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment