Язык это знаковая система...
соглашение о приписывании чему-либо какого-либо определённого смысла
Что такое Предметно-СпецифичныйЯзык?
Язык, который, в противоположность языкам общего назначения
предназначен для решения определенных задач в конкретной предметной области
- Выразительность
- Надежность
- Поддерживаемость
- Семантический Разрыв
- Борьба со сложностью
Насколько можно уменьшить размер наших программ?
Alan Kay / 1000
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver= 4 |IHL= 8 |Type of Service| Total Length = 576 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification = 111 |Flg=0| Fragment Offset = 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time = 123 | Protocol = 6 | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| source address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| destination address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opt. Code = x | Opt. Len.= 3 | option value | Opt. Code = x |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opt. Len. = 4 | option value | Opt. Code = 1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Opt. Code = y | Opt. Len. = 3 | option value | Opt. Code = 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
TCP/IP in 200 lines
Language-OrientedProgramming
lang -> [dsls]* -> solution
FermaT: lisp -> WSL -> metaWSL -> solution
- Design Challenge
- Высокая квалификация
- Производительность
Когда стоит разработать DSL?
- Комбинаторный Взрыв
- Ощущение Рутины
- Все слишком сложно
Классификация
- External (sql, html)
- Embeded (regexp, linq)
- Internal (lisp,ruby/rails,scala)
DSL на данных
- formats (XML, JSON, YAML)
- domain formats (html, xslt, svg)
- meta-data (schemas)
- config files (ini)
LISP homoiconicity
Code is Data
(ast)
function myfn(x,y) | ["function", "myfn",
{ | ["x", "y"],
return | ["+",
x*x + y*y; | ["*", "x", "x"],
} | ["*", "y", "y"]]]
|
myfn(3,6) | ["myfn", "3", "6"]
(ast)
["function", "myfn", | [function myfn
["x", "y"], | [x y]
["+", | [+
["*", "x", "x"], | [* x x]
["*", "y", "y"]]] | [* y y]]]
|
["myfn", "3", "6"] | [myfn 3 6]
(hello lisp)
[function myfn [x y] | (defn myfn [x y]
[+ [* x x] | (+ (* x x)
[* y y]]] | (* y y)))
|
[myfn 3 6] | (myfn 3 6)
punctuation
function myfn(x,y){ | (defn myfn [x y]
return x*x + y*y; | (+ (* x x)
}; | (* y y)))
|
myfn(3,6) | (myfn 3 6)
// 12 [{;,}] | ;; 12 ([])
primitives
nil ;; nil
100 ;; java.lang.Long
0.1 ;; java.lang.Double
"hi" ;;java.lang.String
#"^a.*" ;; java.util.regex.Pattern
:orange ;; clojure.lang.Keyword
'apple ;; clojure.lang.Symbol
Composites
'(1 2 3) ;; PersistentList
[1 2 3] ;; PersistentVector
{:a 1 :b 2} ;; PersistentArrayMap
#{1 2 3} ;; PersistentHashSet
(special-forms)
(def symbol init?)
(if test then else?)
(do exprs*)
(let [bindings* ] exprs*)
(quote form)
(var symbol)
(fn name? [params* ] exprs*)
(loop [bindings* ] exprs*)
(recur exprs*)
(throw expr)
(try expr* catch-clause* finally-clause?)
(and fns macros)
(defn myfn [args] body)
(defmacro mymacro [args] body)
(mymacro (myfn args))
cloc src/
Language files code
-----------------------------
Java: 167 39K
Clojure: 43 16K
SUM: 216 55K
-----------------------------
Scala: 1282 147K
Java: 88 9K
SUM: 1401 163K
-----------------------------
Java: 1020 121K
Groovy: 1335 101K
SUM: 2420 227K
-----------------------------
JRUBY:
Java: 1385 214K
yacc: 3 4K
SUM: 1454 223K
---------------------------
KOTLIN
Java: 1697 187K
SUM: 1863 195K
---------------------------
route-map (sloc 42)
(to-path "GET user/1/new")
=> ["users" 1 "new" :get]
(def routes
{:GET `root
"files" {:path* {:GET `file}}
"users" {:GET `list
:POST `create
[:uid] {:GET `show
:PUT `udpate
:DELETE `destroy}}})
route-map
(route-map/match [:get "/unexisting"] routes) ;;=> nil
(route-map/match [:get "/users/1"] routes)
;;=> {:match 'show
;; :parents [all nodes in path to match]
;; :params {:uid "1"}}
(route-map/match [:get "/files/assets/img/icon.png"] routes)
;;=> {:match 'file
;; :params {:path* ["assets" "img" "icon.png"]}
;; :parents ...}
route-map: extend semantic
(def subroutes {:GET `handler
"subpath" {...}})
{:mw [`ensure-signed-in]
:doc "REST API for ..."
"subroutes" subroutes
"admin" {:mw [`ensure-admin]
:doc "manage ..."
"endpoint" {:GET `handler}}}
;; collect interceptors from
[`ensure-signed-in `ensure-admin]
;; generate documentation/visualize
;; mount routes/modularize
;; use in tests
route-map: interop
(to-json routes)
;; send to client side (js)
;; send to rest clients
Prismatic Schema (sloc 321)
(def Schema
{:a {:b s/Str
:c s/Int}
:d [{:e s/Keyword
:f [s/Num]}]
(s/maybe :g) s/Str})
(s/validate Schema data)
(s/check Schema data)
;; Exception -- Value does not match schema:
;; {:a {:b (not (instance? java.lang.String 123)),
;; :c (not (integer? "ABC"))},
;; :d missing-required-key}
(coerce/coercer Schema json-coercion-matcher)
Mix it
{"entity"
{:POST
{:handler `handler
:doc "bla, bla"
:body EntitySchema
:params ParamsSchema}}}
(to-swagger routes)
;; routing
;; validation
;; coercing
;; meta-data
hiccup (sloc 509)
[:tag {:attr "value"} "content"]
(defn link-to [url txt]
[:a {:href url} lbl])
(def layout [content]
[:html [:head ...] [:body content]])
(layout
[:ul
(for [x xs]
(link-to x))])
(html data)
honeysql (sloc 701)
{:select [:f.*]
:from [[:foo :f] [:baz :b]]
:join [:draq [:= :f.b :draq.x]]
:where [:or
[:and [:= :f.a "bort"]
[:not= :b.baz "var"]]
[:between :f.e 10 20]]
:group-by [:f.a]
:having [:< 0 :f.e]
:limit 50}
(sql/format data) => ["select ? ..." args]
honeysql (sloc 701)
;; sugar dsls
(-> (select :a :b :c)
(from :foo)
(where [:= :f.a "baz"]))
;; composition
(-> sqlmap
(merge-select :d :e)
(merge-where [:> :b 10]))
;; extension
{:select [:*]
:from [:users]
:with [:roles]}
;;=> {:name "user" :roles [{:name "admin"}]}
datalog/datascript
;; query
[:find ?release-name
:in $ ?artist-name
:where [?artist :artist/name ?artist-name]
[?release :release/artists ?artist]
[?release :release/name ?release-name]]
Plumbing graph
(def stats-graph
{:n (fnk [xs] (count xs))
:m (fnk [xs n] (/ (sum identity xs) n))
:m2 (fnk [xs n] (/ (sum #(* % %) xs) n))
:v (fnk [m m2] (- m2 (* m m)))})
(def stats-eager (graph/compile stats-graph))
(def lazy-stats (graph/lazy-compile stats-graph))
(stats-eager {:xs [1 2 3 6]})
(lazy-stats {:xs [1 2 3 6]})
Reciepe
- data format
- interpreter
- sugar
- [compiler]
Data DSL
- simple
- interoperable
- dynamic
- re-interpretable
- extendable
Our app is a languagefor non-programmers
Write code for interpreter
Write interpreter for data