declarative programming & algebraic data types from django's perspective
TRANSCRIPT
![Page 1: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/1.jpg)
Declarative Programming&
Algebraic Data Types *
Maxim Avanovmaximavanov.com
* from Django's perspective
19th Moscow Django Meetup
![Page 2: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/2.jpg)
Our Goal1. «Outsource» boilerplate (i.e. concentrate on important).2. Check as much as possible, and as soon as possible.3. Component coherence.
![Page 3: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/3.jpg)
1. A story of How & WhatConcentrate on important
![Page 4: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/4.jpg)
How vs. Whatdef handle_article_form(request): if request.method == 'POST': form = ArticleForm(request.POST) if form.is_valid(): save_article(form.cleaned_data) return HttpResponseRedirect('/success/') else: form = ArticleForm()
return render(request, 'article_form.html', {'form': form})
![Page 5: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/5.jpg)
Howif request.method == 'POST': form = ArticleForm(request.POST) if form.is_valid(): # ...else: # ...
What# case 1save_article(form.cleaned_data)return HttpResponseRedirect('/success/')
# case 2form = ArticleForm()return render(request, 'article_form.html', {'form': form})
![Page 6: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/6.jpg)
A few things to worry aboutdef handle_article_form(request): if request.method == 'POST': form = ArticleForm(request.POST) if form.is_valid(): save_article(form.cleaned_data) return HttpResponseRedirect('/success/') else: form = ArticleForm()
return render(request, 'article_form.html', {'form': form})
«What» is obscured by «How»redundant detailsSingle Responsibility principle is violated
![Page 7: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/7.jpg)
If we could get rid of How's once and for all,would we miss them?
![Page 8: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/8.jpg)
Constraint programming
![Page 9: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/9.jpg)
Tribute to Pyramidfrom rhetoric import view_config, view_defaults
@view_defaults(route_name='articles', renderer='article_form.html')class ArticlesHandler(object): def __init__(self, request): self.request = request
@view_config(request_method='GET') def article_form(self): form = ArticleForm() return {'form': form}
@view_config(request_method='POST', validate_form=ArticleForm) def save_article(self): save_article(self.request.form.cleaned_data) return HttpResponseRedirect('/success/')
@view_config(request_method='POST') def invalid_article_form(self): return {'form': self.request.form}
![Page 10: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/10.jpg)
What it actually means# We have an Articles Handler: ArticlesHandler# It renders a template: article_form.html# A user shall be able to add new entries: article_form()# If we submit valid ArticleForm: save_article()# If we submit invalid ArticleForm: invalid_article_form()
class ArticlesHandler(object): def __init__(self, request): self.request = request
def article_form(self): form = ArticleForm() return {'form': form}
def save_article(self): save_article(self.request.form.cleaned_data) return HttpResponseRedirect('/success/')
def invalid_article_form(self): return {'form': self.request.form}
![Page 11: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/11.jpg)
...or in other words---view: ArticlesHandlerGET: article_formPOST: - validate: articles.ArticleForm view: save_article - view: invalid_article_form
class ArticlesHandler(object): def __init__(self, request): self.request = request
def article_form(self): form = ArticleForm() return {'form': form}
def save_article(self): save_article(self.request.form.cleaned_data) return HttpResponseRedirect('/success/')
def invalid_article_form(self): return {'form': self.request.form}
![Page 12: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/12.jpg)
A couple of other examples
![Page 13: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/13.jpg)
Different ways to do the same thing@view_defaults(route_name='authentication', renderer='auth_form.html')class AuthenticationHandler(object):
@view_config(request_method='POST', validate_form=EmailAuthForm) def auth_with_email(self): # ...
@view_config(request_method='POST', validate_form=SMSAuthForm) def auth_with_sms(self): # ...
@view_config(request_method='POST', validate_form=LoginAuthForm) def auth_with_login(self): # ...
@view_config(request_method='POST') def on_invalid_form(self): # ...
![Page 14: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/14.jpg)
API versioningconfig.add_route('api.workflows', '/api/workflows')
@view_defaults(route_name='api.workflows', api_version='<2.0')class WorkflowsAPIv1(object): # ...
@view_defaults(route_name='api.workflows', api_version='>=2.0')class WorkflowsAPIv2(object): # ...
@view_defaults(route_name='api.workflows', renderer='json')class WorkflowsAPI(object):
@view_config(request_method='POST', api_version='<2.0') def create_new_workflow_v1(self): # ...
@view_config(request_method='POST', api_version='>=2.0') def create_new_workflow_v2(self): # ...
![Page 15: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/15.jpg)
Algebraic Data Types
![Page 16: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/16.jpg)
OCaml ADT exampleWatch a «Caml Trading» talk by Yaron Minsky at
http://youtu.be/hKcOkWzj0_s?t=31m6s
![Page 17: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/17.jpg)
OCaml ADT exampletype order = { id: int; price: float; size: int; }type cancel = { xid: int; }
type instruction = | Order of order | Cancel of cancel
let filter_by_oid instructions oid = List.filter instructions (fun x -> match x with | Order o -> o.id = oid | Cancel c -> c.xid = oid)
![Page 18: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/18.jpg)
OCaml ADT exampletype order = { id: int; price: float; size: int; }type cancel = { xid: int; }
type cancel_replace = { xr_id: int; new_price: float; new_size: int; }
type instruction = | Order of order | Cancel of cancel | Cancel_replace of cancel_replace
let filter_by_oid instructions oid = List.filter instructions (fun x -> match x with | Order o -> o.id = oid | Cancel c -> c.xid = oid)
Warning P: This pattern-matching is not exhaustiveHere is an example of a value that is not matched...
![Page 19: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/19.jpg)
2. Trying to reproduceCheck as much as possible, and as soon as possible
![Page 20: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/20.jpg)
Python ADT *
* kind of
![Page 21: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/21.jpg)
Python ADT example
Models (product types)
from django.db import models
class Order(models.Model): tid = models.IntegerField() price = models.DecimalField(max_digits=16, decimal_places=4) size = models.IntegerField()
class Cancel(models.Model): xtid = models.IntegerField()
class CancelReplace(models.Model): xr_tid = models.IntegerField() new_price = models.DecimalField(max_digits=16, decimal_places=4) new_size = models.IntegerField()
![Page 22: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/22.jpg)
Python ADT example
Smart Enums (union types)
from rhetoric.adt import adtfrom .models import Order, Cancel, CancelReplace
class Instruction(adt): ORDER = Order CANCEL = Cancel CANCEL_REPLACE = CancelReplace
![Page 23: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/23.jpg)
Python ADT example
Cases (match statement)
from .types import Instruction
@Instruction.ORDER('filter_by_oid')def filter_order_by_oid(order, oid): return order.tid == oid
@Instruction.CANCEL('filter_by_oid')def filter_cancel_by_oid(cancel, oid): return cancel.xtid == oid
@Instruction.CANCEL_REPLACE('filter_by_oid')def filter_cancel_replace_by_oid(cr, oid): return cr.xr_tid == oid
def filter_by_oid(instructions, oid): return list(filter( lambda i: Instruction.match(i)['filter_by_oid'](i, oid), instructions))
![Page 24: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/24.jpg)
Python ADT example
Cases (2)
from .types import Instruction
inline_matcher = Instruction.inline_match( ORDER = (lambda o, oid: o.tid == oid), CANCEL = (lambda c, oid: c.xtid == oid), CANCEL_REPLACE = (lambda cr, oid: cr.xr_tid == oid))
def filter_by_oid_alt(instructions, oid): return list(filter(lambda i: inline_matcher(i)(i, oid), instructions))
![Page 25: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/25.jpg)
3. Use case: multilingual contentComponent coherence
![Page 26: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/26.jpg)
Use case
Define ADT
from rhetoric.adt import adt
class Language(adt): ENGLISH = 'en' GERMAN = 'de'
![Page 27: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/27.jpg)
Use case
Register Models
from django.db import modelsfrom .types import Language
class IRegionalArticle(models.Model): class Meta: abstract = True title = models.CharField(max_length=140, default='') text = models.TextField(max_length=65536, default='')
@Language.ENGLISH('db:articles')class EnglishArticle(IRegionalArticle): pass
@Language.GERMAN('db:articles')class GermanArticle(IRegionalArticle): pass
![Page 28: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/28.jpg)
Use case
Process requests
from rhetoric.view import view_config, view_defaultsfrom .types import Language
@view_defaults(route_name='articles.regional.index', renderer='json')class ArticlesHandler(object): def __init__(self, request, language): self.request = request self.language = language self.language_strategy = Language.match(language)
@view_config(request_method='GET') def show_local_entries(self): return {'ok': True}
![Page 29: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/29.jpg)
Consistency check
Adding a new language
class Language(adt): ENGLISH = 'en' GERMAN = 'de' SPANISH = 'es'
ConfigurationError: Case db:articles of <class 'project.articles.types.Language'> is not exhaustive. Here is the variant that is not matched: SPANISH
# We have to register this [email protected]('db:articles')class SpanishArticle(IRegionalArticle): pass
![Page 30: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/30.jpg)
Consistency check
Using undefined variant
from rhetoric.adt import adt
class Language(adt): ENGLISH = 'en' GERMAN = 'de'
inline_matcher = Language.inline_match( ENGLISH = lambda: EnglishArticle.objects.all(), GERMAN = lambda: GermanArticle.objects.all(), SPANISH = lambda: SpanishArticle.objects.all())
PatternError: Variant SPANISH does not belong to the type <class 'project.articles.types.Language'>
![Page 31: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/31.jpg)
Consistency check
Guard boundaries
from .types import Language
def includeme(config): RULES = {'language': Language} config.add_route('articles.regional.index', '/articles/{language}', RULES)
/articles/{language} => ̂articles/(?P<language>(?:de|en))$
class Language(adt): ENGLISH = 'en' GERMAN = 'de' SPANISH = 'es'
/articles/{language} => ̂articles/(?P<language>(?:de|en|es))$
![Page 32: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/32.jpg)
Use case
Strategy map
class ArticlesHandler(object): def __init__(self, request, language): self.request = request self.language = language self.language_strategy = Language.match(language)
GET /articles/de{'db:articles': <class 'project.articles.models.GermanArticle'>}
GET /articles/en{'db:articles': <class 'project.articles.models.EnglishArticle'>}
![Page 33: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/33.jpg)
Beyond today's topicThings worth mentioning
github.com/lihaoyi/macropygithub.com/benanhalt/PyAlgebraicDataTypes (cons ADT)«Say What You Mean» talk by Ryan Kelly
![Page 34: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/34.jpg)
Thank you
Q & A
![Page 35: Declarative Programming & Algebraic Data Types from Django's perspective](https://reader033.vdocument.in/reader033/viewer/2022052906/55897457d8b42aa94a8b46ac/html5/thumbnails/35.jpg)
Credits talk by Yaron Minsky«Caml Trading»
Rhetoric ProjectVenusian Project