[FT-7][snowmantw] How to make a new functional language and make the world better
1. How to make a new language
and make the world better
...or, let's talk about eDSLs
Greg Weng
about.me/snowmantw
snowmantw@gmail.com
bit.ly/edsl-intro
8. embedded DSL means
"...implemented as libraries which exploit the
syntax of their host general purpose language or
a subset thereof, while adding domain-specific
language elements (data types, routines, methods,
macros etc.)."
From Wikipedia (eDSL)
9. You might already used some of
these eDSLs ...
$('#message')
.val("Winston Smith...")
.fadeOut('slow')
.hide( )
.val("Big Brother Is Watching You")
.css('font-color', 'red')
.show( )
var $message = document.getElementById('message')
$message.value = "Winston Smith..."
fadeOut($message, 'slow')
hide($message)
$message.value = "Big Brother is Watching You"
$message.style.frontColor = 'red'
show($message)
jQuery
10. You might already used some of
these eDSLs ...
var stooges = [{name : 'curly', age : 25}, {name :
'moe', age : 21}, {name : 'larry', age : 23}]
var youngest = _.chain(stooges)
.sortBy(function(stooge)
{ return stooge.age })
.map(function(stooge)
{ return stooge.name + ' is ' + stooge.age })
.first()
.value();
var stooges = [{name : 'curly', age : 25}, {name :
'moe', age : 21}, {name : 'larry', age : 23}]
stooges.sort( function(stooge)
{ return stooge.age } )
var sorted = [ ]
stooges.forEach( function(e,i,x)
{ result[i] = e.name + 'is' + e,age } )
var yougest = sorted[0]
underscore.js
11. You might already used some of
these eDSLs ...
query.from(customer)
.orderBy(customer.lastName.asc()
,customer.firstName.asc())
.list(customer.firstName
,customer.lastName);
// Well, I don't want to handle SQL strings in Java
// manually...
// The left will generate SQL like this:
SELECT c.first_name, c.last_name
FROM customer c
ORDER BY c.last_name ASC, c.first_name ASC
LINQ (Java porting)
12. You might already used some of
these eDSLs ...
select $
from $ (s, e) -> do
where_ (s ^. StockId ==. e ^. EndOfDayStockId &&.
s ^. StockTicker ==. val ticker &&.
s ^. EndOfDayTradeDate ==. val stockDate)
return (e ^. EndOfDayClosingPrice,
e ^. EndOfDayTradeDate)
SELECT end_of_day.closing_price,
end_of_day.trade_date
FROM stock, end_of_day
WHERE stock.stock_id = end_of_day.
stock_id AND (stock.ticker = ? AND
end_of_day.trade_date = ?)
esqueleto
(Haskell)
13. You might already used some of
these eDSLs ...
var mtxA = [ [ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ] ]
var mtxB = [ [ -1],
[ 0],
[ 1] ]
var mtxC = mul( mtxA, mtxB)
var mtxA = [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]
var mtxB = [ [ -1], [ 0], [ 1] ]
var mtxC = mul( mtxA, mtxB)
Matrix
Manipulations
14. eDSLs may (or may not) make your
code shorter and more elegant.
But the most important thing is it helps
you to focus on the current domain
problem with right tools.
$('#message')
.val("Winston Smith...")
.fadeOut('slow')
.hide( )
.val("Big Brother Is Watching You")
.css('font-color', 'red')
.show( )
var $message = document.getElementById('message')
$message.value = "Winston Smith..."
fadeOut($message, 'slow')
hide($message)
$message.value = "Big Brother is Watching You"
$message.style.frontColor = 'red'
show($message)
15. eDSLs and their domain problems
jQuery DOM manipulation
Underscore.js Computation
LINQ Querying
esqueleto Database Querying
Matrix
Manipulations
Arithemetic (Matrix)
16. My small but useful (?) eDSL
Gist: bit.ly/sntw-mtx
mtx( 1, 2, 3)
( 4, 5, 6)
( 7, 8, 9)
(10,11,12).
.mul(11,12,13,14)
(14,15,16,15)
(17,18,19,16)
.mul( 3)
( 4)
( 5)
( 6)
.get()
1. Because using Arrays to represent Matrices
is too mainstream.
2. You don't need the stupid [[outer] [brackets]]
anymore!
3. To test my "inifinite curry" in this example.
17. Syntax
Well, it might be more formal and like a
real language if we can show
something hard to understand...
jsprog := JSStatements prog JSStatements
prog := mtx manipulations get
mtx := mtx rows
rows := row rows
| row
row := ( JSNumber, ..., JSNumber )
get := .get ()
mul := .mul rows
manipulations := mul
/* Can add more manipulations if we need */
20. The triangle relation between following
3 eDSLs:
Or at least its my goal...
jQuery
UI, IO and other computations
with side-effects
Underscore.js
Pure computations like
map-reduce
Fluorine
Isolate impure computation
and combine them with pure
ones reasonably.
21. The triangle relation between following
3 eDSLs:
Or at least its my goal...
// Data -> UI DOM
function recover(dataset)
{
return $('#form-wrapper')
.find('#cover')
.hide()
.end()
.find('#form')
.find('input')
.each(/*fill dataset in*/)
.end()
.fadeIn('slow')
.end()
}
// Use function as argument.
fetch_data(URL, recover)
// URL -> IO Data
function fetch_data(url, cb)
{
let parse =
function(html,cb)
{
$vals = $(html)
.find('input')
.val()
return _
.chain($vals)
.map(/*...*/)
.reduce(/* ... */)
.value()
cb($vals)
}
$.get(url, parse)
// IO is async
}
Impure
Pure
22. The triangle relation between following
3 eDSLs:
Or at least its my goal...
// Data -> UI DOM
function recover(dataset)
{
return $('#form-wrapper')
.find('#cover')
.hide()
.end()
.find('#form')
.find('input')
.each(/*fill dataset in*/)
.end()
.fadeIn('slow')
.end()
}
// Use function as argument.
fetch_data(URL, recover)
// URL -> IO Data
function fetch_data(url, cb)
{
let parse =
function(html,cb)
{
$vals = $(html)
.find('input')
.val()
return _
.chain($vals)
.map(/*...*/)
.reduce(/* ... */)
.value()
cb($vals)
}
$.get(url, parse)
// IO is async
}
// URL -> IO DOM
var recover = IO(URL)
.get()
.tie(function(html)
{ return UI()
.find('input')
.val()
})
._(parse) // IO HTML -> IO Data
.tie(recover) // IO Data -> IO DOM
.idgen()
// recover :: ( ) -> IO DOM
// recover():: IO DOM
var main = recover()
// Run the "Monad", runIO
main()
23. JavaScript lacks these features to
become more "functional"
PDF: bit.ly/js-fun
From one of my representations in JS Group
1. (Has) First class function & anonymous function
2. (Could) Curry & Partial Application
3. (Poorly) Supports recursion
4. (Isn't) Pure
5. (Isn't) Lazy
The most lethal one is that you can't isolate impure code
as nature as in Haskell.
24. With Fluorine you can:
GitHub: bit.ly/fluorine-js
1. Isolate impure parts in the program
2. Mix pure/impure when necessary
3. Flow-Control, to avoid callback hell
4. Laziness (well, sort of)
28. UI context
Customized Process
initialize
Step #1
Step #2
Step #3
Step #4
......
done
Process The type of our contexts are actually
m (Process a)
rather than
m a
They all have implicit process
29. UI context
Customized Process
initialize
Step #1
Step #2
Step #3
Step #4
......
done
Process
IO context
initialize
Step #1
get
tie
post
......
done
Process
flattern callback hell
reasonably
Process helps us
(Pure code never goes async)
30. Customized Process
IO context
initialize
Step #1
get
tie
post
......
done
Process You can extract result from UI and other
context (remember Maybe or List?)
var foo = IO().get()....done()().
extract()
// Don't do this.
Because of its internal aschronous
process may return wrong value
However, IO is not "co-context"
You should never extract things from IO
31. Customized Process
IO context
initialize
Step #1
get
tie
post
......
done
Process For example, this is legal and safe:
var foo = IO().get()....done()().
extract()
However, you should never:
var foo = UI('#foo').$().done()().
extract()
m = head ([1,2,3] >>= return.odd)
Just like
Just like
m = unsafePerformIO (getChar >> getChar)
32. Customized Process
IO context
initialize
Step #1
get
tie
post
......
done
Process In Haskell, things should never escape
from IO monad due to the side-effects
It's occasionally true in Fluorine. And we
both have very good reasons to say that
33. The definition and running stage
IO context
initialize
Step #1
get
tie
post
......
done
Process IO.o.prototype.get
definition
➔ Setup the step
from the context
➔ Push the step to
the process
(runtime stack)
➔ Return this to
keep chaining
running
➔ Pop one step from
the stack
➔ Execute it with the
environment
➔ Capture its return
value and pass to
the next
➔ Call next when
this one is done
34. The definition and running stage
IO context
initialize
Step #1
get
tie
post
......
done
Process This stage would capture information from
the context to create the step
It's also possible to do more check before
we enter the running stage
IO().get('/todo').tie(renew_ui).post
('/ok')
.done()
For example, typed JavaScript?
Thus we don't have compilation time,
we have definition time
35. The definition and running stage
IO context
initialize
Step #1
get
tie
post
......
done
Process The key is "to call the next when this one
is done" in runtime.
Thus, the |get| step can call the next |tie|
step only after its remote request has
returned success.
This empower us to make customized
binding function, just like the different
>>= among Haskell monads.
36. Tying
IO context
initialize
Step #1
get
tie
post
......
done
Process Tying means you tied another context into
the current one
In theory, it's very simple: just push
another context generator into the stack
It's an unsuccessful attempt to mimic the
>>= function in Monad Transformer
tie:: m a -> (a -> n b) -> m b
>>=:: M m a -> (a -> M m b) -> M m b
37. Tying
IO context
initialize
Step #1
get
tie
post
......
done
Process
Note the tied context would take over the
control of the whole process:
IO().get('/todo')
.tie(function(todos)
{
return Event('user-request')
.tie(ui_render(todos))
.done()
})
.post('/ok')
.done()
The |post| won't be executed til the event
|user-request| fired, because it's Event's
default behavior
40. What will you get and lose if you
force your eDSL compatible with
harsh Lint(s)
Too bad...
1. You CAN'T figure out what's going wrong at first look
2. You CAN'T indetify symbols and strings anymore
3. Damn semicolons in a language needn't them
41. Here are you only choices...
To be or not to be,
that is the question
(Hard work) Make your own Lint
(Bad) Ignore the Lint
45. Of course you must provide a
serious tool, not dangerous IEDs
46. Of course you must provide a
serious tool, not dangerous IEDs
Remember, you are desinging a
REAL programming language !
In my (painful) experience:
1. debugger, debugger, debugger
2. useful examples
3. simple introducation & detailed API/technical documents
4. eat your own dog food
47. Even one of those math-like most
language allows users create and
use eDSL
JavaScript, Ruby, Java...
48. Why forcing people to drive a car only as
a car, when it's actually a Transformer ?