Created
April 11, 2017 05:27
-
-
Save tatut/00b7d9a08550ef25d651906ecdcedc88 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns m | |
(:require [clojure.java.io :as io] | |
[clojure.pprint :refer [*print-suppress-namespaces* pprint]] | |
[clojure.repl :refer [doc source]])) | |
;; Utility ennen/jälkeen tulosteille | |
(defmacro m [form] | |
(binding [*print-suppress-namespaces* true] | |
(println "\n----ENNEN:" ) | |
(pprint form) | |
(println "----JÄLKEEN:") | |
(pprint (macroexpand-1 form)))) | |
;; Mikä on makro? | |
;; Ohjelma, joka ajetaan käännösaikana. | |
;; Ottaa sisään parametrit (joita ei ole evaluoitu) | |
;; Tuottaa lähdekoodin, jolla kääntäjä korvaa makrokutsun | |
;; Voidaan käyttää esim. source transformaatioon | |
;; Yksinkertainen esimerkki | |
(defmacro lisaa-yksi [x] | |
`(do | |
;; risuaita tekee uuden nimen, jota voidaan käyttää turvallisesti | |
;; ~x on unquote, joka korvaa kohdan tuloksessa x:llä | |
(let [x# ~x] | |
(println "Hei, lisää yhden arvoon " x#) | |
(let [tulos# (+ x# 1)] | |
(println "Saatiin arvo " tulos#) | |
tulos#)))) | |
(lisaa-yksi 1) | |
(def x 665) | |
(lisaa-yksi x) | |
;; nimen x käyttö on turvallista eikä sekoitu | |
(m (lisaa-yksi x)) | |
;; Mitä jos halutaan tarkoituksella kaapata sama nimi? | |
;; Mahdollista, mutta kääntäjä estää sen vahingossa tekemisen | |
(defmacro ei-toimi [foo] | |
`(let [tulos (* ~foo 2)] | |
tulos)) | |
(ei-toimi 123) | |
(m (ei-toimi 123)) | |
;; unquote/quote yhdistelmällä se onnistuu | |
(defmacro toimii [foo] | |
`(let [~'tulos (* ~foo)] | |
~'tulos)) | |
(toimii 123) | |
(m (toimii 123)) | |
;; with- makrot | |
;; yleensä tekevät jotain ympäristön alustusta, ajavat käyttäjän antaman koodin ja | |
;; lopuksi tekevät ympäristön siivoamisen | |
(with-open [f (io/reader "https://google.com")] | |
(println (take 1 (line-seq f)))) | |
(m (with-open [foo bar] baz)) | |
;; def- makrot | |
;; yleensä ottavat nimen ja määrittelyn ja määrittelevät annetun nimisen | |
;; nimen nimiavaruuteen | |
(defmulti foo :type) | |
(m (defmulti foo :type)) | |
(m (defmethod foo :bar [_] (println "Baz!"))) | |
;; Defrecord luo uuden tyypin sekä konstruktoreita | |
(defrecord Quux [zoo]) | |
(->Quux 123) | |
(map->Quux {:zoo 666}) | |
(m (defrecord Quux [zoo])) | |
;; Makrot voivat tehdä mitä vaan, joten ne eivät rajoitu annetun lähdekoodin | |
;; muokkaamiseen, vaan voivat lukea mitä vain. | |
;; esim Harjassa, "luetaan lähdekoodia" | |
;; - excel tiedostosta | |
;; - json skeematiedostoista | |
;; - tietokannasta | |
;; esimerkki 1: excel tiedosto | |
(require '[harja.domain.oikeudet.makrot :refer [maarittele-oikeudet!]]) | |
(m (maarittele-oikeudet!)) | |
;; esimerkki 2: json skeematiedostoista | |
(require '[webjure.json-schema.validator.macro :refer [make-validator]]) | |
(require '[cheshire.core :as cheshire]) | |
(def skeema (cheshire/parse-string (slurp "skeema.json"))) | |
(def v (make-validator skeema {})) | |
(pprint (v (cheshire/parse-string | |
"{\"address\": {\"city\": \"Oulu\", \"streetAddress\": \"Torikatu 18\"}, \"phoneNumber\": [-1]}"))) | |
(pprint (v 123)) | |
;; Generoitu koodi on pitkä | |
(m (make-validator skeema {})) | |
;; esimerkki 3: tietokannasta | |
(require '[specql.core :refer [define-tables]]) | |
(require '[harja.domain.tierekisteri :as tr]) | |
(require '[harja.domain.urakka :as urakka]) | |
(m (define-tables {:connection-uri "jdbc:postgresql://localhost/harjatest_template?user=postgres"} | |
["tr_osoite" ::tr/osoite] | |
["urakkatyyppi" ::urakka/urakkatyyppi])) | |
;; lisää hassuja makroja | |
(def *sarcasm* true) | |
;; ärsyttääkö, että let vaatii "turhat" sulut, ei hätää! korjaa se itse | |
(defmacro lets [& bindings-ja-form] | |
`(let [~@(butlast bindings-ja-form)] | |
~(last bindings-ja-form))) | |
(lets | |
x 1 y 2 | |
(+ x y)) | |
(m (lets | |
x 1 y 2 | |
(+ x y))) | |
;; onko rajapinta epävakaa, ei se mitään yritä uudestaan vaan! | |
(defn tryhard* [how-hard form on-failure] | |
(let [ex (gensym "EX")] | |
`(try | |
~form | |
(catch Throwable ~ex | |
~(if (zero? how-hard) | |
`(~on-failure ~ex) | |
(tryhard* (dec how-hard) form on-failure)))))) | |
(defmacro tryhard [how-hard form on-failure] | |
(tryhard* how-hard form on-failure)) | |
(m (tryhard 4 (/ 10 0) (fn [ex] | |
(println "ei onnistu: " ex)))) | |
(tryhard 5 | |
(let [luku (Math/random)] | |
(if (> luku 0.2) | |
(throw (RuntimeException. "ei kelepaa")) | |
luku)) | |
(fn [ex] | |
(println "ei saatu sopivaa lukua!"))) | |
;; huom: ylempi olisi parempi kirjoittaa siten, että "yritys" on | |
;; funktio, eikä expandoida samaa koodia n kertaa | |
;; core.async | |
(require '[clojure.core.async :as async :refer [go <! >! chan timeout]]) | |
;; go on massiivisen monimutkaisen makro, joka on käytännössä clojure compiler | |
;; muuntaa normaalin clojure koodin tilakoneeksi, joka ei ole ajossa kun se | |
;; odottaa kanavaoperaatioita | |
(async/<!! (go | |
(println "odotellaan sekunti") | |
(<! (timeout 1000)) | |
(println "odoteltiin") | |
(System/currentTimeMillis))) | |
(m (go | |
(println "odotellaan sekunti") | |
(<! (timeout 1000)) | |
(println "odoteltiin") | |
(System/currentTimeMillis))) | |
(defn printtaa [x] | |
(println "X ON " x)) | |
(with-out-str | |
(println "FOO") | |
(future (printtaa 42)) | |
(println "BAR")) | |
(m (and 1 2)) | |
(defn ja [& arvot] | |
(every? identity arvot)) | |
(def x 42) | |
(m (cond | |
(even? x) :on-se | |
(odd? x) :ei-se-oo | |
:default :jotain-outoa)) | |
(defmacro foo [& args] | |
(let [{:keys [nimi maarittely]} (s/conform ::foo args)] | |
(assert nimi "virheilmoitus") | |
`(do ...koodi...))) | |
(deftest mun-makro | |
(is (thrown-with-msg? | |
AssertionError #"virhe" | |
(= '(foo) (macroexpand '(mun-makro-kutsu 1 2 3)))))) | |
(defmacro mittaa-aika [& body] | |
`(let [alku# (System/currentTimeMillis)] | |
(try | |
(if (< (Math/random) 0.1) | |
(throw (RuntimeException. "ei onnistu"))) | |
~@body | |
(finally | |
(println "KESTI: " (- (System/currentTimeMillis) alku#)))))) | |
(defmacro oma+ [x y] | |
(list '+ x y)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment