Data DSLs



Data DSLs

0 0


data-dsl-2015-slides


On Github niquola / data-dsl-2015-slides

Data DSLs

Created by niquola / @niquola

Что такое Язык?

Язык это знаковая система...

Что такое Знак?

соглашение о приписывании чему-либо какого-либо определённого смысла

Что такое Предметно-СпецифичныйЯзык?

Язык, который, в противоположность языкам общего назначения предназначен для решения определенных задач в конкретной предметной области

Примеры DSL?

  • SQL
  • HTML/CSS
  • BASH
  • ...

Зачем DSL?

  • Выразительность
  • Надежность
  • Поддерживаемость
  • Семантический Разрыв
  • Борьба со сложностью

Насколько можно уменьшить размер наших программ?

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

Чем плох DSL?

  • 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 ([])

Clojure in 2 min

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

Code & Data

Our app is a languagefor non-programmers

Write code for interpreter

Write interpreter for data

Write your own langs!

Thx

Q?

Data DSLs Created by niquola / @niquola