SlideShare a Scribd company logo
1 of 43
Download to read offline
Designing
with
Malli
London Clojurians
12.08.2020
Tommi Reiman
@ikitommi
a new data validation and specification
library for Clojure/Script. It provides unified
tools for building data-driven schema systems
covering schema definition, validation, (human)
errors, value and schema transformation, value and
schema generation, registries and much more.
https://github.com/metosin/malli
Malli
(def address
{:id "123"
:tags #{:garage :chicken}
:address {:street "Jl. Pacar No.15, Sumerta Kauh"
:city "Densapar"
:zip 80236
:latlon [-8.652037 115.226808]}})
data
(def Address
{:id s/Str
:tags #{s/Keyword}
:address {:street s/Str
:city s/Str
:zip s/Int
:latlon [(s/one Double "lat")
(s/one Double "lon")]}})
plumatic schema
(require '[clojure.spec.alpha :as s])
(s/def ::id string?)
(s/def ::tags (s/coll-of keyword? :kind set?))
(s/def ::street string?)
(s/def ::city string?)
(s/def ::zip int?)
(s/def ::latlon (s/tuple double? double?))
(s/def :address/address
(s/keys :req-un [::street ::city ::zip ::latlon]))
(s/def ::address
(s/keys :req-un [::id ::tags :address/address]))
clojure.spec
(def Address
[:map
[:id string?]
[:tags [:set keyword?]]
[:address
[:map
[:street string?]
[:city string?]
[:zip int?]
[:latlon [:tuple double? double?]]]]])
Hiccup syntax
Just data and functions
Maps are open
by default
Keys are
required by
default
Malli
Ordered
Keys
(def Address
[:map
[:id string?]
[:tags [:set keyword?]]
[:address
[:map
[:street string?]
[:city string?]
[:zip int?]
[:latlon [:tuple double? double?]]]]])
Malli
(def Address
[:map {:title “Address”
:summary “An address"}
[:id string?]
[:tags {:optional true} [:set keyword?]]
[:address
[:map {:closed true}
[:street string?]
[:city string?]
[:zip {:default "33800"} int?]
[:latlon [:tuple double? double?]]]]])
Malli
Schema applications
• Validation and Machine-readable errors
• Spell-checking and human-readable errors
• Two-way value transformations
• Schema transformations (walkers, visitors, utils, JSON Schema, DOT, ..)
• GeneratingValues & Schemas
• (Parsing)
(def invalid-address
{:id 123
:tags #{:garage :chicken}
:address {:streetz "Jl. Pacar No.15"
:city "Densapar"
:latlon [-8.652037 115.226808]}})
(-> Address
(m/explain invalid-address)
(me/with-spell-checking)
(me/humanize))
;{:id ["should be a string"]
; :address {:zip ["missing required key"]
; :streetz ["should be spelled :street"]}}
(require '[malli.generator :as mg])
(mg/generate Address {:size 10, :seed 20})
;{:id "A1f05U4uE5",
; :tags #{:FqNN/Ti. :b3/h. :Ni/h? :Yg*-/GW*Y :jhL/k! :_/QnR},
; :address {:street "ABM74Q96S4"
; :city "k6z3OD4cy"
; :zip -1
; :latlon [-0.32421875 -1.8125]}}
The
Registry
Registries
• Schema types are looked from a registry
• Registry is passed as explicit option into schema functions
• Default registry has ~100 types and is immutable
• Other registry types: mutable, dynamic, lazy, local and composite
• Special JVM / Closure compiler option to swap the default registry
• Default to global mutable registry in Malli 1.0.0?
(def Order
[:schema
{:registry {"Country" [:map
[:name [:enum :FI :PO]]
[:neighbors [:vector [:ref "Country"]]]]
"Burger" [:map
[:name string?]
[:description {:optional true} string?]
[:origin [:maybe "Country"]]
[:price pos-int?]]
"OrderLine" [:map
[:burger "Burger"]
[:amount int?]]
"Order" [:map
[:lines [:vector "OrderLine"]]
[:delivery [:map
[:delivered boolean?]
[:address [:map
[:street string?]
[:zip int?]
[:country "Country"]]]]]]}}
"Order"])
Decomplect
maps, keys
and values
::id int?
::id int?
(s/def :address/id string?)
(s/def :address/tags (s/coll-of keyword? :kind set?))
(s/def :address/street string?)
(s/def :address/city string?)
(s/def :address/zip int?)
(s/def :address/latlon (s/tuple double? double?))
(s/def :address.address/address
(s/keys :req [::street ::city ::zip ::latlon]))
(s/def :address/address
(s/keys :req [::id ::tags :address/address]))
[:schema
{:registry {::id string?
::tags [:set keyword?]
::street string?
::city string?
::zip int?
::latlon [:tuple double? double?]
:address/address [:map ::street ::city ::zip ::latlon]
::address [:map ::id ::tags :address/address]}}
::address]
[:map
{:registry {::id string?
::tags [:set keyword?]
::address [:map
{:registry {::street string?
::city string?
::zip int?
::latlon [:tuple double? double?]}}
::street ::city ::zip ::latlon]}}
::id ::tags ::address]
In Real
World
Coercion
• We are dealing with various data sources with different capabilities,
e.g. JSON,YAML and XML data
• We need to decode from X to EDN and encode from EDN to X’
• … or just apply default values. Or convert strings into keyword keys
• Malli has fast two-way interceptor-based transformers
• Transforming based on Schema type and properties
(def Address
[:map {:title “Address”
:summary “An address"}
[:id string?]
[:tags {:optional true} [:set keyword?]]
[:address
[:map {:closed true}
[:street string?]
[:city string?]
[:zip {:default "33800"} int?]
[:latlon [:tuple double? double?]]]]])
(require '[malli.transform :as mt])
(m/decode
Address
{:id "123"
:tags [“garage” “chicken”]
:address {:street "Jl. Pacar No.15"
:city "Densapar"
:zip 80236
:latlon [-8.652037 115.226808]}}
(mt/json-transformer))
;{:id "123"
; :tags #{:garage :chicken},
; :address {:street "Jl. Pacar No.15, Sumerta Kauh, Kec."
; :city "Densapar"
; :zip 80236
; :latlon [-8.652037 115.226808]}}
(m/decode
Address
{:id "Lillan"
:EVIL "LYN"
:tags ["coffee" "artesan" "garden"]
:address {:street "Ahlmanintie 29"
:DARK "ORKO"
:city "Tampere"
:lonlat [61.4858322 23.7854658]}}
(mt/transformer
(mt/strip-extra-keys-transformer)
(mt/default-value-transformer)
(mt/json-transformer)))
;{:id "Lillan"
; :tags #{:coffee :artesan :garden}
; :address {:street "Ahlmanintie 29"
; :city "Tampere"
; :zip "33800"}}
(def LegacyString
[:string {:decode/string '(fn [x] (str/lower-case (subs x 4)))
:encode/string '(fn [x] (str/upper-case (str "HAL_" x)))}])
(m/decode LegacyString "HAL_KIKKA" (mt/string-transformer))
; => "kikka"
(m/encode LegacyString "kikka" (mt/string-transformer))
; => "HAL_KIKKA"
(m/decode
[:string {:decode/string {:enter '#(str "olipa_" %)
:leave '#(str % "_avaruus")}}]
"kerran"
(mt/string-transformer))
; => "olipa_kerran_avaruus"
Inferring values
• Opposite of GeneratingValues - return a Schema that validates
against the sample values
• Crawled legacy MongoDB collection (5+ years of accumulated stuff)
• For each value, inferred a schema + accumulated schema union
• All documents have timestamps —>Temporal Schema index!
(require '[malli.provider :as mp])
(mp/provide
[{:id "123"
:tags #{:garage :chicken}
:address {:street "Jl. Pacar No.15, Sumerta Kauh, Kec."
:city "Densapar"
:zip 80236
:latlon [-8.652037 115.226808]}}
{:id "Lillan"
:tags #{:coffee :artesan :garden}
:address {:street "Ahlmanintie 29"
:city "Tampere"
:lonlat [61.4858322 23.7854658]}}])
;[:map
; [:id string?]
; [:tags [:set keyword?]]
; [:address
; [:map
; [:street string?]
; [:city string?]
; [:zip {:optional true} int?]
; [:latlon {:optional true} [:vector double?]]
; [:lonlat {:optional true} [:vector double?]]]]]
Serialising Schemas
• Thanks to SCI (https://github.com/borkdude/sci) and Edamame
(https://github.com/borkdude/edamame), mostly all schemas can be
• Thanks to SCI (https://github.com/borkdude/sci) and Edamame
(https://github.com/borkdude/edamame), mostly all schemas can be
persisted (and read back) to EDN
• Works with all of clj (JVM & GraalVM) and cljs, no eval needed
• Big leverage in building truly dynamic systems
• Playground at malli.io
(def Adult
[:map {:registry {::age [:and int? [:> 18]]}}
[:age ::age]])
(-> Adult
(malli.edn/write-string)
(malli.edn/read-string)
(m/validate {:age 46}))
; => true
Programming
with Schemas
malli.util
• Schemas can be transformed, just like data
• Pre- & postwalk for generic walking over schemas
• update-properties, get, assoc, update, get-in, assoc-in, update-in, dissoc,
select-keys, …
• subschemas for
• Backed by the Schema & LensSchema protocols
Dev-
tooling
Function Schemas
• (Higher order) functions can also be described with Malli
• Any existing ClojureVar can be annotated with Malli Schemas
• Support multi-rarities with custom return values for each
• Does not support varargs (WIP, the sequence schemas)
• Integrates to CLJ-kondo, plumatic schema style inline schemas WIP
• Aave, Ghostwheel-port for Malli (https://github.com/teknql/aave)
CLJS
• Designed to be light, DCE can remove most of the unneeded core
• Starts from few kilos zipped (validation with minimal registry)
GraalVM
• Works on GraalVM too, binaries starting from 14mb
Malli 1.0.0
Malli 1.0.0
• Support for sequence schemas (WIP, Pauli)
• Support for Schema parsing
• Protocol-based Fast Schema Inferrer
• Describe Malli Schemas with Malli (recall spec-of-specs)
• Expound-grade pretty printer (WIP)
• Proper documentation
• Re-visit some design decision (including the global registry)
Wrap-up
Designing with Malli
• Where was the designing part??? ;)
• Malli in a clean slate fusion of the best parts of existing libraries
(plumatic schema and clojure.spec)
• In alpha, under active development
• Lot’s of ideas, so little (extra) time, help welcome
• Try reitit-malli to get started…

More Related Content

What's hot

Mastering Java Bytecode With ASM - 33rd degree, 2012
Mastering Java Bytecode With ASM - 33rd degree, 2012Mastering Java Bytecode With ASM - 33rd degree, 2012
Mastering Java Bytecode With ASM - 33rd degree, 2012
Anton Arhipov
 

What's hot (20)

從線上售票看作業系統設計議題
從線上售票看作業系統設計議題從線上售票看作業系統設計議題
從線上售票看作業系統設計議題
 
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
 
Mastering Java Bytecode With ASM - 33rd degree, 2012
Mastering Java Bytecode With ASM - 33rd degree, 2012Mastering Java Bytecode With ASM - 33rd degree, 2012
Mastering Java Bytecode With ASM - 33rd degree, 2012
 
Getting Started with Confluent Schema Registry
Getting Started with Confluent Schema RegistryGetting Started with Confluent Schema Registry
Getting Started with Confluent Schema Registry
 
A Beginner's Guide to Building Data Pipelines with Luigi
A Beginner's Guide to Building Data Pipelines with LuigiA Beginner's Guide to Building Data Pipelines with Luigi
A Beginner's Guide to Building Data Pipelines with Luigi
 
PromQL Deep Dive - The Prometheus Query Language
PromQL Deep Dive - The Prometheus Query Language PromQL Deep Dive - The Prometheus Query Language
PromQL Deep Dive - The Prometheus Query Language
 
Pe Format
Pe FormatPe Format
Pe Format
 
Hexagonal architecture in PHP
Hexagonal architecture in PHPHexagonal architecture in PHP
Hexagonal architecture in PHP
 
Lab1-DB-Cassandra
Lab1-DB-CassandraLab1-DB-Cassandra
Lab1-DB-Cassandra
 
Event-sourced architectures with Akka
Event-sourced architectures with AkkaEvent-sourced architectures with Akka
Event-sourced architectures with Akka
 
Rust vs C++
Rust vs C++Rust vs C++
Rust vs C++
 
Bytecode manipulation with Javassist and ASM
Bytecode manipulation with Javassist and ASMBytecode manipulation with Javassist and ASM
Bytecode manipulation with Javassist and ASM
 
Introducing BinarySortedMultiMap - A new Flink state primitive to boost your ...
Introducing BinarySortedMultiMap - A new Flink state primitive to boost your ...Introducing BinarySortedMultiMap - A new Flink state primitive to boost your ...
Introducing BinarySortedMultiMap - A new Flink state primitive to boost your ...
 
What's new in Scala 2.13?
What's new in Scala 2.13?What's new in Scala 2.13?
What's new in Scala 2.13?
 
Practical learnings from running thousands of Flink jobs
Practical learnings from running thousands of Flink jobsPractical learnings from running thousands of Flink jobs
Practical learnings from running thousands of Flink jobs
 
Play with FILE Structure - Yet Another Binary Exploit Technique
Play with FILE Structure - Yet Another Binary Exploit TechniquePlay with FILE Structure - Yet Another Binary Exploit Technique
Play with FILE Structure - Yet Another Binary Exploit Technique
 
MongoDB Advanced Schema Design - Inboxes
MongoDB Advanced Schema Design - InboxesMongoDB Advanced Schema Design - Inboxes
MongoDB Advanced Schema Design - Inboxes
 
Domain Driven Design Made Functional with Python
Domain Driven Design Made Functional with Python Domain Driven Design Made Functional with Python
Domain Driven Design Made Functional with Python
 
Introduction to DataFusion An Embeddable Query Engine Written in Rust
Introduction to DataFusion  An Embeddable Query Engine Written in RustIntroduction to DataFusion  An Embeddable Query Engine Written in Rust
Introduction to DataFusion An Embeddable Query Engine Written in Rust
 
Redis in Practice
Redis in PracticeRedis in Practice
Redis in Practice
 

Similar to Designing with malli

Building Analytics Applications with Streaming Expressions in Apache Solr - A...
Building Analytics Applications with Streaming Expressions in Apache Solr - A...Building Analytics Applications with Streaming Expressions in Apache Solr - A...
Building Analytics Applications with Streaming Expressions in Apache Solr - A...
Lucidworks
 
ql.io: Consuming HTTP at Scale
ql.io: Consuming HTTP at Scale ql.io: Consuming HTTP at Scale
ql.io: Consuming HTTP at Scale
Subbu Allamaraju
 

Similar to Designing with malli (20)

Reitit - Clojure/North 2019
Reitit - Clojure/North 2019Reitit - Clojure/North 2019
Reitit - Clojure/North 2019
 
Parallel SQL and Analytics with Solr: Presented by Yonik Seeley, Cloudera
Parallel SQL and Analytics with Solr: Presented by Yonik Seeley, ClouderaParallel SQL and Analytics with Solr: Presented by Yonik Seeley, Cloudera
Parallel SQL and Analytics with Solr: Presented by Yonik Seeley, Cloudera
 
ELK Stack - Turn boring logfiles into sexy dashboard
ELK Stack - Turn boring logfiles into sexy dashboardELK Stack - Turn boring logfiles into sexy dashboard
ELK Stack - Turn boring logfiles into sexy dashboard
 
State of the Art Web Mapping with Open Source
State of the Art Web Mapping with Open SourceState of the Art Web Mapping with Open Source
State of the Art Web Mapping with Open Source
 
Megamodeling of Complex, Distributed, Heterogeneous CPS Systems
Megamodeling of Complex, Distributed, Heterogeneous CPS SystemsMegamodeling of Complex, Distributed, Heterogeneous CPS Systems
Megamodeling of Complex, Distributed, Heterogeneous CPS Systems
 
Big Data LDN 2017: Processing Fast Data With Apache Spark: the Tale of Two APIs
Big Data LDN 2017: Processing Fast Data With Apache Spark: the Tale of Two APIsBig Data LDN 2017: Processing Fast Data With Apache Spark: the Tale of Two APIs
Big Data LDN 2017: Processing Fast Data With Apache Spark: the Tale of Two APIs
 
SVGD3Angular2React
SVGD3Angular2ReactSVGD3Angular2React
SVGD3Angular2React
 
Scalding big ADta
Scalding big ADtaScalding big ADta
Scalding big ADta
 
Real-Time Analytics with Solr: Presented by Yonik Seeley, Cloudera
Real-Time Analytics with Solr: Presented by Yonik Seeley, ClouderaReal-Time Analytics with Solr: Presented by Yonik Seeley, Cloudera
Real-Time Analytics with Solr: Presented by Yonik Seeley, Cloudera
 
Om nom nom nom
Om nom nom nomOm nom nom nom
Om nom nom nom
 
Angular2 for Beginners
Angular2 for BeginnersAngular2 for Beginners
Angular2 for Beginners
 
Reigniting the API Description Wars with TypeSpec and the Next Generation of ...
Reigniting the API Description Wars with TypeSpec and the Next Generation of...Reigniting the API Description Wars with TypeSpec and the Next Generation of...
Reigniting the API Description Wars with TypeSpec and the Next Generation of ...
 
Azure F#unctions
Azure F#unctionsAzure F#unctions
Azure F#unctions
 
Streaming Solr - Activate 2018 talk
Streaming Solr - Activate 2018 talkStreaming Solr - Activate 2018 talk
Streaming Solr - Activate 2018 talk
 
Building Analytics Applications with Streaming Expressions in Apache Solr - A...
Building Analytics Applications with Streaming Expressions in Apache Solr - A...Building Analytics Applications with Streaming Expressions in Apache Solr - A...
Building Analytics Applications with Streaming Expressions in Apache Solr - A...
 
2019 hashiconf consul-templaterb
2019 hashiconf consul-templaterb2019 hashiconf consul-templaterb
2019 hashiconf consul-templaterb
 
Dublin Ireland Spark Meetup October 15, 2015
Dublin Ireland Spark Meetup October 15, 2015Dublin Ireland Spark Meetup October 15, 2015
Dublin Ireland Spark Meetup October 15, 2015
 
ql.io: Consuming HTTP at Scale
ql.io: Consuming HTTP at Scale ql.io: Consuming HTTP at Scale
ql.io: Consuming HTTP at Scale
 
Clojure ♥ cassandra
Clojure ♥ cassandra Clojure ♥ cassandra
Clojure ♥ cassandra
 
The Aggregation Framework
The Aggregation FrameworkThe Aggregation Framework
The Aggregation Framework
 

More from Metosin Oy

More from Metosin Oy (18)

Navigating container technology for enhanced security by Niklas Saari
Navigating container technology for enhanced security by Niklas SaariNavigating container technology for enhanced security by Niklas Saari
Navigating container technology for enhanced security by Niklas Saari
 
Where is Technical Debt?
Where is Technical Debt?Where is Technical Debt?
Where is Technical Debt?
 
Creating an experimental GraphQL formatter using Clojure, Instaparse, and Gra...
Creating an experimental GraphQL formatter using Clojure, Instaparse, and Gra...Creating an experimental GraphQL formatter using Clojure, Instaparse, and Gra...
Creating an experimental GraphQL formatter using Clojure, Instaparse, and Gra...
 
Serverless Clojure and ML prototyping: an experience report
Serverless Clojure and ML prototyping: an experience reportServerless Clojure and ML prototyping: an experience report
Serverless Clojure and ML prototyping: an experience report
 
Naked Performance With Clojure
Naked Performance With ClojureNaked Performance With Clojure
Naked Performance With Clojure
 
Fun with errors? - Clojure Finland Meetup 26.3.2019 Tampere
Fun with errors? - Clojure Finland Meetup 26.3.2019 TampereFun with errors? - Clojure Finland Meetup 26.3.2019 Tampere
Fun with errors? - Clojure Finland Meetup 26.3.2019 Tampere
 
Clojutre Real Life (2012 ClojuTRE Retro Edition)
Clojutre Real Life (2012 ClojuTRE Retro Edition)Clojutre Real Life (2012 ClojuTRE Retro Edition)
Clojutre Real Life (2012 ClojuTRE Retro Edition)
 
The Ancient Art of Data-Driven - reitit, the library -
The Ancient Art of Data-Driven - reitit, the library - The Ancient Art of Data-Driven - reitit, the library -
The Ancient Art of Data-Driven - reitit, the library -
 
Craft Beer & Clojure
Craft Beer & ClojureCraft Beer & Clojure
Craft Beer & Clojure
 
Performance and Abstractions
Performance and AbstractionsPerformance and Abstractions
Performance and Abstractions
 
ClojuTRE2016 Opening slides
ClojuTRE2016 Opening slidesClojuTRE2016 Opening slides
ClojuTRE2016 Opening slides
 
Schema tools-and-trics-and-quick-intro-to-clojure-spec-22.6.2016
Schema tools-and-trics-and-quick-intro-to-clojure-spec-22.6.2016Schema tools-and-trics-and-quick-intro-to-clojure-spec-22.6.2016
Schema tools-and-trics-and-quick-intro-to-clojure-spec-22.6.2016
 
ClojuTRE - a (very) brief history
ClojuTRE - a (very) brief historyClojuTRE - a (very) brief history
ClojuTRE - a (very) brief history
 
Wieldy remote apis with Kekkonen - ClojureD 2016
Wieldy remote apis with Kekkonen - ClojureD 2016Wieldy remote apis with Kekkonen - ClojureD 2016
Wieldy remote apis with Kekkonen - ClojureD 2016
 
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesome
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesomeClojuTRE2015: Kekkonen - making your Clojure web APIs more awesome
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesome
 
Clojure in real life 17.10.2014
Clojure in real life 17.10.2014Clojure in real life 17.10.2014
Clojure in real life 17.10.2014
 
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesomeEuroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
 
Swaggered web apis in Clojure
Swaggered web apis in ClojureSwaggered web apis in Clojure
Swaggered web apis in Clojure
 

Recently uploaded

%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
masabamasaba
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
masabamasaba
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
masabamasaba
 

Recently uploaded (20)

%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
%+27788225528 love spells in Huntington Beach Psychic Readings, Attraction sp...
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptx
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
WSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security ProgramWSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security Program
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
WSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - KeynoteWSO2Con204 - Hard Rock Presentation - Keynote
WSO2Con204 - Hard Rock Presentation - Keynote
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaS
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
 
%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 

Designing with malli

  • 2. a new data validation and specification library for Clojure/Script. It provides unified tools for building data-driven schema systems covering schema definition, validation, (human) errors, value and schema transformation, value and schema generation, registries and much more. https://github.com/metosin/malli Malli
  • 3. (def address {:id "123" :tags #{:garage :chicken} :address {:street "Jl. Pacar No.15, Sumerta Kauh" :city "Densapar" :zip 80236 :latlon [-8.652037 115.226808]}}) data
  • 4. (def Address {:id s/Str :tags #{s/Keyword} :address {:street s/Str :city s/Str :zip s/Int :latlon [(s/one Double "lat") (s/one Double "lon")]}}) plumatic schema
  • 5. (require '[clojure.spec.alpha :as s]) (s/def ::id string?) (s/def ::tags (s/coll-of keyword? :kind set?)) (s/def ::street string?) (s/def ::city string?) (s/def ::zip int?) (s/def ::latlon (s/tuple double? double?)) (s/def :address/address (s/keys :req-un [::street ::city ::zip ::latlon])) (s/def ::address (s/keys :req-un [::id ::tags :address/address])) clojure.spec
  • 6. (def Address [:map [:id string?] [:tags [:set keyword?]] [:address [:map [:street string?] [:city string?] [:zip int?] [:latlon [:tuple double? double?]]]]]) Hiccup syntax Just data and functions Maps are open by default Keys are required by default Malli Ordered Keys
  • 7. (def Address [:map [:id string?] [:tags [:set keyword?]] [:address [:map [:street string?] [:city string?] [:zip int?] [:latlon [:tuple double? double?]]]]]) Malli
  • 8. (def Address [:map {:title “Address” :summary “An address"} [:id string?] [:tags {:optional true} [:set keyword?]] [:address [:map {:closed true} [:street string?] [:city string?] [:zip {:default "33800"} int?] [:latlon [:tuple double? double?]]]]]) Malli
  • 9. Schema applications • Validation and Machine-readable errors • Spell-checking and human-readable errors • Two-way value transformations • Schema transformations (walkers, visitors, utils, JSON Schema, DOT, ..) • GeneratingValues & Schemas • (Parsing)
  • 10. (def invalid-address {:id 123 :tags #{:garage :chicken} :address {:streetz "Jl. Pacar No.15" :city "Densapar" :latlon [-8.652037 115.226808]}}) (-> Address (m/explain invalid-address) (me/with-spell-checking) (me/humanize)) ;{:id ["should be a string"] ; :address {:zip ["missing required key"] ; :streetz ["should be spelled :street"]}}
  • 11. (require '[malli.generator :as mg]) (mg/generate Address {:size 10, :seed 20}) ;{:id "A1f05U4uE5", ; :tags #{:FqNN/Ti. :b3/h. :Ni/h? :Yg*-/GW*Y :jhL/k! :_/QnR}, ; :address {:street "ABM74Q96S4" ; :city "k6z3OD4cy" ; :zip -1 ; :latlon [-0.32421875 -1.8125]}}
  • 13. Registries • Schema types are looked from a registry • Registry is passed as explicit option into schema functions • Default registry has ~100 types and is immutable • Other registry types: mutable, dynamic, lazy, local and composite • Special JVM / Closure compiler option to swap the default registry • Default to global mutable registry in Malli 1.0.0?
  • 14. (def Order [:schema {:registry {"Country" [:map [:name [:enum :FI :PO]] [:neighbors [:vector [:ref "Country"]]]] "Burger" [:map [:name string?] [:description {:optional true} string?] [:origin [:maybe "Country"]] [:price pos-int?]] "OrderLine" [:map [:burger "Burger"] [:amount int?]] "Order" [:map [:lines [:vector "OrderLine"]] [:delivery [:map [:delivered boolean?] [:address [:map [:street string?] [:zip int?] [:country "Country"]]]]]]}} "Order"])
  • 15.
  • 17. (s/def :address/id string?) (s/def :address/tags (s/coll-of keyword? :kind set?)) (s/def :address/street string?) (s/def :address/city string?) (s/def :address/zip int?) (s/def :address/latlon (s/tuple double? double?)) (s/def :address.address/address (s/keys :req [::street ::city ::zip ::latlon])) (s/def :address/address (s/keys :req [::id ::tags :address/address]))
  • 18. [:schema {:registry {::id string? ::tags [:set keyword?] ::street string? ::city string? ::zip int? ::latlon [:tuple double? double?] :address/address [:map ::street ::city ::zip ::latlon] ::address [:map ::id ::tags :address/address]}} ::address]
  • 19. [:map {:registry {::id string? ::tags [:set keyword?] ::address [:map {:registry {::street string? ::city string? ::zip int? ::latlon [:tuple double? double?]}} ::street ::city ::zip ::latlon]}} ::id ::tags ::address]
  • 21. Coercion • We are dealing with various data sources with different capabilities, e.g. JSON,YAML and XML data • We need to decode from X to EDN and encode from EDN to X’ • … or just apply default values. Or convert strings into keyword keys • Malli has fast two-way interceptor-based transformers • Transforming based on Schema type and properties
  • 22. (def Address [:map {:title “Address” :summary “An address"} [:id string?] [:tags {:optional true} [:set keyword?]] [:address [:map {:closed true} [:street string?] [:city string?] [:zip {:default "33800"} int?] [:latlon [:tuple double? double?]]]]])
  • 23. (require '[malli.transform :as mt]) (m/decode Address {:id "123" :tags [“garage” “chicken”] :address {:street "Jl. Pacar No.15" :city "Densapar" :zip 80236 :latlon [-8.652037 115.226808]}} (mt/json-transformer)) ;{:id "123" ; :tags #{:garage :chicken}, ; :address {:street "Jl. Pacar No.15, Sumerta Kauh, Kec." ; :city "Densapar" ; :zip 80236 ; :latlon [-8.652037 115.226808]}}
  • 24. (m/decode Address {:id "Lillan" :EVIL "LYN" :tags ["coffee" "artesan" "garden"] :address {:street "Ahlmanintie 29" :DARK "ORKO" :city "Tampere" :lonlat [61.4858322 23.7854658]}} (mt/transformer (mt/strip-extra-keys-transformer) (mt/default-value-transformer) (mt/json-transformer))) ;{:id "Lillan" ; :tags #{:coffee :artesan :garden} ; :address {:street "Ahlmanintie 29" ; :city "Tampere" ; :zip "33800"}}
  • 25. (def LegacyString [:string {:decode/string '(fn [x] (str/lower-case (subs x 4))) :encode/string '(fn [x] (str/upper-case (str "HAL_" x)))}]) (m/decode LegacyString "HAL_KIKKA" (mt/string-transformer)) ; => "kikka" (m/encode LegacyString "kikka" (mt/string-transformer)) ; => "HAL_KIKKA" (m/decode [:string {:decode/string {:enter '#(str "olipa_" %) :leave '#(str % "_avaruus")}}] "kerran" (mt/string-transformer)) ; => "olipa_kerran_avaruus"
  • 26.
  • 27. Inferring values • Opposite of GeneratingValues - return a Schema that validates against the sample values • Crawled legacy MongoDB collection (5+ years of accumulated stuff) • For each value, inferred a schema + accumulated schema union • All documents have timestamps —>Temporal Schema index!
  • 28. (require '[malli.provider :as mp]) (mp/provide [{:id "123" :tags #{:garage :chicken} :address {:street "Jl. Pacar No.15, Sumerta Kauh, Kec." :city "Densapar" :zip 80236 :latlon [-8.652037 115.226808]}} {:id "Lillan" :tags #{:coffee :artesan :garden} :address {:street "Ahlmanintie 29" :city "Tampere" :lonlat [61.4858322 23.7854658]}}]) ;[:map ; [:id string?] ; [:tags [:set keyword?]] ; [:address ; [:map ; [:street string?] ; [:city string?] ; [:zip {:optional true} int?] ; [:latlon {:optional true} [:vector double?]] ; [:lonlat {:optional true} [:vector double?]]]]]
  • 29. Serialising Schemas • Thanks to SCI (https://github.com/borkdude/sci) and Edamame (https://github.com/borkdude/edamame), mostly all schemas can be • Thanks to SCI (https://github.com/borkdude/sci) and Edamame (https://github.com/borkdude/edamame), mostly all schemas can be persisted (and read back) to EDN • Works with all of clj (JVM & GraalVM) and cljs, no eval needed • Big leverage in building truly dynamic systems • Playground at malli.io
  • 30. (def Adult [:map {:registry {::age [:and int? [:> 18]]}} [:age ::age]]) (-> Adult (malli.edn/write-string) (malli.edn/read-string) (m/validate {:age 46})) ; => true
  • 32. malli.util • Schemas can be transformed, just like data • Pre- & postwalk for generic walking over schemas • update-properties, get, assoc, update, get-in, assoc-in, update-in, dissoc, select-keys, … • subschemas for • Backed by the Schema & LensSchema protocols
  • 33.
  • 34.
  • 36. Function Schemas • (Higher order) functions can also be described with Malli • Any existing ClojureVar can be annotated with Malli Schemas • Support multi-rarities with custom return values for each • Does not support varargs (WIP, the sequence schemas) • Integrates to CLJ-kondo, plumatic schema style inline schemas WIP • Aave, Ghostwheel-port for Malli (https://github.com/teknql/aave)
  • 37. CLJS • Designed to be light, DCE can remove most of the unneeded core • Starts from few kilos zipped (validation with minimal registry)
  • 38. GraalVM • Works on GraalVM too, binaries starting from 14mb
  • 39.
  • 41. Malli 1.0.0 • Support for sequence schemas (WIP, Pauli) • Support for Schema parsing • Protocol-based Fast Schema Inferrer • Describe Malli Schemas with Malli (recall spec-of-specs) • Expound-grade pretty printer (WIP) • Proper documentation • Re-visit some design decision (including the global registry)
  • 43. Designing with Malli • Where was the designing part??? ;) • Malli in a clean slate fusion of the best parts of existing libraries (plumatic schema and clojure.spec) • In alpha, under active development • Lot’s of ideas, so little (extra) time, help welcome • Try reitit-malli to get started…