SlideShare a Scribd company logo
1 of 61
Download to read offline
DSL в Python.
Цыганов Иван
Positive Technologies
Как и зачем?
– Мартин Фаулер
Предметно ориентированный язык — это
язык программирования с ограниченными
выразительными возможностями,
ориентированный на некую конкретную
предметную область
SQL
SELECT * FROM Users WHERE Age>20
SQL
SELECT * FROM Users WHERE Are>20
REGEXP
[A-Z][A-Za-z0-9]*
SQL
REGEXP
[A-Z]w+
LaTeX
E &=& mc^2
SQL
REGEXP
LaTeX
E &=& mc^2
HTML
<a href='http://piterpy.ru'>PiterPy</a>
SQL
REGEXP
LaTeX
HTML
<a href='http://piterpy.ru'>PiterPy</a>
PonyORM
select(p for p in Person if p.age > 20)
SQL
REGEXP
LaTeX
HTML
PonyORM
WTForms
class UsernameForm(Form):

username = StringField('Username')
✤ SQL
✤ REGEXP
✤ TeX/LaTeX
✤ HTML
Виды DSL
DSL
ВнутренниеВнешние
✤ PonyORM
✤ WTForm
✤ Django models
Высокая скорость разработки
import re



html_source = '''...'''



links = re.findall(

pattern=r'''<a href=("|')(?P<URL>.*?)1>(?P<Text>.*?)</a>''',

string=html_source
Дополнительный уровень абстракции
persons = select(p for p in Person if p.age > 20)[:]
Код могут писать "не-программисты"
server {
location / {
proxy_pass http://localhost:8080/;
}
location ~ .(gif|jpg|png)$ {
root /data/images;
}
}
Проблемы и решения
Высокая стоимость разработки DSL
Проблемы и решения
Достаточно один раз разобраться
Высокая стоимость разработки DSL
Проблемы и решения
Достаточно один раз разобраться
Высокая стоимость разработки DSL
Нет специалистов со знанием языка
Проблемы и решения
Достаточно один раз разобраться
Высокая стоимость разработки DSL
Язык должен иметь ограниченные возможности
Нет специалистов со знанием языка
Проблемы и решения
Достаточно один раз разобраться
Высокая стоимость разработки DSL
Язык должен иметь ограниченные возможности
Нет специалистов со знанием языка
Работает не для всех задач
Проблемы и решения
Достаточно один раз разобраться
Высокая стоимость разработки DSL
Язык должен иметь ограниченные возможности
Нет специалистов со знанием языка
Стоит попробовать, что бы понять
Работает не для всех задач
Перейдем к практике
Зачем нужна модель
✤ Хранит в себе всю бизнес-логику
✤ Позволяет использовать различные DSL
✤ Обеспечивает возможность тестирования
Семантическая модель
Внутренние DSL
Внутренние DSL
Все возможности
базового языка
Привычный синтаксис
Легко работать
Ограничен базовым
языком
Цепочки вызовов
✤ Все методы заполняют модель и возвращают объект
✤ Методы именуются исходя из смыслового контекста
FileUpdater()

.path('music')

.mask('.*metallica.*')

.set(Genre='Rock')

.set(Artist='Metallica'

.do()
Вложенные функции
✤ Для заполнения модели вызываются функции
✤ Функции именуются исходя из смыслового
контекста
update(

settings(

path('./music'),

mask('.*.mp3')

),

set(Artist='Metallica'),

set(Genre='Rock')

)

Import hooks
Очень мощный
инструмент
Вмешиваемся в работу
интерпретатора
Очень сложная отладка
Непредсказуемые side-
эффекты
Import hooks
Не используйте это в реальном мире!
PathFinder
FileLoader
import requests
def source_to_code(self, data, path, *, _optimize=-1):

…
source = self.get_source(path)

return compile(

source,
path,
…

)
WITH ".*.mp3"

IN "../tests/music/"

SET Artist="Metallica"

SET Genre="Rock"
my_script.py
import internal.import_tokenizer

import examples.internal_data.my_script as script



script.task.process_rules()
import my_script as script
PathFinder
FileLoader
def source_to_code(self, data, path, *, _optimize=-1):

…
tokens = translate(path)

return compile(

tokenize.untokenize(tokens),
path,
…

)
def translate(path):

tokens = tokenize.generate_tokens(…)

while tokens:
...

if token_value in ('IN', ‘WITH'):
...

yield from create_task()
...

elif ...

else:

yield (tok_type, value)
def translate(path):

tokens = tokenize.generate_tokens(…)

while tokens:
...

if token_value in ('IN', ‘WITH'):
...

yield from create_task()
...

elif ...

else:

yield (tok_type, value)
yield from create_task()
def create_task():

yield from [

(tokenize.NAME, 'from'),

(tokenize.NAME, 'model'),

(tokenize.NAME, 'import'),

(tokenize.NAME, 'Task'),

(tokenize.OP, ','),

(tokenize.NAME, 'Rule'),

(tokenize.NEWLINE, 'n'),

(tokenize.NAME, 'task'),

(tokenize.OP, '='),

(tokenize.NAME, 'Task'),

(tokenize.OP, '('),

(tokenize.OP, ')'),

(tokenize.NEWLINE, 'n')

]
def create_task():

yield from [

(tokenize.NAME, 'from'),

(tokenize.NAME, 'model'),

(tokenize.NAME, 'import'),

(tokenize.NAME, 'Task'),

(tokenize.OP, ','),

(tokenize.NAME, 'Rule'),

(tokenize.NEWLINE, 'n'),

(tokenize.NAME, 'task'),

(tokenize.OP, '='),

(tokenize.NAME, 'Task'),

(tokenize.OP, '('),

(tokenize.OP, ')'),

(tokenize.NEWLINE, 'n')

]
DSL Ruby
task = Task.new do
with '*..mp3'
inside './music'
rule do
Artist 'Metallica'
end
rule do
Genre 'Rock'
end
end
task.run()
Внешние DSL
Внешние DSL
Нет базового языка
Сами выбираем
синтаксис
Нет базового языка
Необходимо
разрабатывать
анализаторы
Свой язык
WITH ".*.mp3"
IN "./music"
SET Artist="Metallica"
SET Genre="Rock"
Python Lex-Yacc
(PLY)
ply.lex
ply.yacc
ASTRunCode
source
ply.lex
WITH ".*.mp3"
IN "./music"
SET Artist="Metallica"
SET Genre="Rock"
Type Value
WITH WITH
IN IN
SET SET
EQUALS =
VALUE ".*?"
ATTRIBUTE [A-Za-z][A-Za-z0-9]*
ply.lex
WITH ".*.mp3"
IN "./music"
SET Artist="Metallica"
SET Genre="Rock"
SET EQUALS
SET Artist="Metallica"
ATTRIBUTE
Artist
VALUE
"Metallica"
ply.lex WITH Artist
IN "Metallica"
LexToken(WITH,'WITH',1,0)
LexToken(ATTRIBUTE,'Artist',1,5)
LexToken(IN,'IN',1,12)
LexToken(VALUE,'"Metallica"',1,15)
ply.yacc
WITH ".*.mp3"
IN "./music"
SET Artist="Metallica"
SET Genre="Rock"
rule : SET ATTRIBUTE EQUALS VALUE
with : WITH VALUE
in : IN VALUE
rule_list : rule_list rule
| rule
task : with in rule_list
| in rule_list
PLY
Генерируем AST
ply.yacc
WITH ".*.mp3"
IN "./music"
SET Artist="Metallica"
SET Genre="Rock"
simple_token = namedtuple(

'simple_token', ['Name', 'Value']

)



def p_rule(self, p):

'''rule : SET ATTRIBUTE EQUALS VALUE'''

p[0] = simple_token(Name='RULE', Value=(p[2], p[4]))
ply.yacc
WITH ".*.mp3"
IN "./music"
SET Artist="Metallica"
SET Genre="Rock"
Task
With In RuleList
Rule Rule.*.mp3 "./music"
Artist Genre"Metallica" "Rock"
PLY
Заполняем модель
ply.yacc
WITH ".*.mp3"
IN "./music"
SET Artist="Metallica"
SET Genre="Rock"
def p_rule(p):

'''rule : SET ATTRIBUTE EQUALS VALUE'''

p[0] = Rule(**{p[2]: p[4]})
ply.yacc
Task
root_dir = "./music"

file_mask = ".*.mp3"

rules = [
]
Rule
Artist = "Metallica"
Rule
Genre = "Rock"
WITH ".*.mp3"
IN "./music"
SET Artist="Metallica"
SET Genre="Rock"
PLY
Гибкость
Отладка
Обработка ошибок
Понятный код
библиотеки
Высокий порог входа
Многословный
funcparserlib
funcparserlib
WITH ".*.mp3"
IN "./music"
SET Artist="Metallica"
SET Genre="Rock"
task = Task()



root = keyword('In') + value_of('Value') >> set_root

mask = keyword('With') + value_of('Value') >> set_mask

rule = keyword('Set') + 

value_of('Attribute') + 

keyword('Equals') + 

value_of('Value') 

>> make_rule



parser = maybe(mask) + root + many(rule)

parser.parse(source)
funcparserlib
IN "./music"
WITH ".*.mp3"
SET Artist="Metallica"
SET Genre="Rock"
get_value = lambda x: x.value

value_of = lambda t: some(lambda x: x.type == t) >> get_value

keyword = lambda s: skip(value_of(s))



set_root = lambda value: task.set_root_dir(value[1:-1])

set_mask = lambda value: task.set_mask(value[1:-1])
make_rule = lambda x: task.add_rule(Rule(**{x[0]: x[1]}))
funcparserlib
Компактный
Гибкий
Для любителей
функционального
программирования :)
Многое приходится
делать руками
Для любителей
функционального
программирования :)
pyparsing
pyparsing
WITH ".*.mp3"
IN "./music"
SET Artist="Metallica"
SET Genre="Rock"
rule = (

Keyword('SET') +

Word(alphanums)('key') +

'=' +

QuotedString('"')('value')

).setParseAction(lambda r: {r.key: r.value})
>>> rule.parseString('SET Artist="Metallica"')
{'Artist': 'Metallica'}
pyparsing
WITH ".*.mp3"
IN "./music"
SET Artist="Metallica"
SET Genre="Rock"
{

'mask': '.*.mp3',

'root_dir': './music',

'rules': [

{'Artist': 'Metallica'},

{'Genre': 'Rock'}

]

}
pyparsing
Понятная грамматика
Базовые компоненты
Свои компоненты
Постобработка каждого
элемента
Документация
Отладка
Если сомневаетесь в необходимости DSL -
попробуйте
Начните с семантической модели, это
ведь просто библиотека
mi.0-0.im

More Related Content

What's hot

О безопасном использовании PHP wrappers
О безопасном использовании PHP wrappersО безопасном использовании PHP wrappers
О безопасном использовании PHP wrappers
Positive Hack Days
 
DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6
Dmitry Soshnikov
 
Юрий Гольцев - Сервис PLWWW
Юрий Гольцев - Сервис PLWWWЮрий Гольцев - Сервис PLWWW
Юрий Гольцев - Сервис PLWWW
Positive Hack Days
 
plwww (24.03) MEPHI (PHDays)
plwww (24.03) MEPHI (PHDays)plwww (24.03) MEPHI (PHDays)
plwww (24.03) MEPHI (PHDays)
ygoltsev
 
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
Badoo Development
 

What's hot (20)

"Web Audio Api", Анатолий Найда, MoscowJS 27
"Web Audio Api", Анатолий Найда, MoscowJS 27"Web Audio Api", Анатолий Найда, MoscowJS 27
"Web Audio Api", Анатолий Найда, MoscowJS 27
 
"Деплой кода процедур" Мурат Кабилов (Avito)
"Деплой кода процедур" Мурат Кабилов (Avito)"Деплой кода процедур" Мурат Кабилов (Avito)
"Деплой кода процедур" Мурат Кабилов (Avito)
 
Hacking PostgreSQL. Локальная память процессов. Контексты памяти.
Hacking PostgreSQL. Локальная память процессов. Контексты памяти.Hacking PostgreSQL. Локальная память процессов. Контексты памяти.
Hacking PostgreSQL. Локальная память процессов. Контексты памяти.
 
Reform: путь к лучшему ORM
Reform: путь к лучшему ORMReform: путь к лучшему ORM
Reform: путь к лучшему ORM
 
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
Aleksei Milovidov "Let's optimize one aggregate function in ClickHouse"
 
О безопасном использовании PHP wrappers
О безопасном использовании PHP wrappersО безопасном использовании PHP wrappers
О безопасном использовании PHP wrappers
 
Erlang tasty & useful stuff
Erlang tasty & useful stuffErlang tasty & useful stuff
Erlang tasty & useful stuff
 
Something about Golang
Something about GolangSomething about Golang
Something about Golang
 
DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6
 
PHP basic
PHP basicPHP basic
PHP basic
 
Юрий Гольцев - Сервис PLWWW
Юрий Гольцев - Сервис PLWWWЮрий Гольцев - Сервис PLWWW
Юрий Гольцев - Сервис PLWWW
 
plwww (24.03) MEPHI (PHDays)
plwww (24.03) MEPHI (PHDays)plwww (24.03) MEPHI (PHDays)
plwww (24.03) MEPHI (PHDays)
 
Почему Mojolicious?
Почему Mojolicious?Почему Mojolicious?
Почему Mojolicious?
 
Александр Сомов "C++: препроцессор, компилятор, компоновщик"
Александр Сомов "C++: препроцессор, компилятор, компоновщик"Александр Сомов "C++: препроцессор, компилятор, компоновщик"
Александр Сомов "C++: препроцессор, компилятор, компоновщик"
 
Perl 5.10 и 5.12
Perl 5.10 и 5.12Perl 5.10 и 5.12
Perl 5.10 и 5.12
 
Perl – жив?!
Perl – жив?!Perl – жив?!
Perl – жив?!
 
Nginx.pm
Nginx.pmNginx.pm
Nginx.pm
 
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
"Почему язык Lua — это интересно?", Ник Заварицкий, (Mail.ru Group)
 
Present saint-per3-by-pavel-vlasov
Present saint-per3-by-pavel-vlasovPresent saint-per3-by-pavel-vlasov
Present saint-per3-by-pavel-vlasov
 
Erlang и n2o. Web-разработка без JavaScript
Erlang и n2o. Web-разработка без JavaScriptErlang и n2o. Web-разработка без JavaScript
Erlang и n2o. Web-разработка без JavaScript
 

Viewers also liked

Intercepter-NG: сниффер нового поколения
Intercepter-NG: сниффер нового поколенияIntercepter-NG: сниффер нового поколения
Intercepter-NG: сниффер нового поколения
Positive Hack Days
 
Алексей Ионов, Agile на раз-два
Алексей Ионов, Agile на раз-дваАлексей Ионов, Agile на раз-два
Алексей Ионов, Agile на раз-два
ScrumTrek
 
Presentation at the conference
Presentation at the conferencePresentation at the conference
Presentation at the conference
Vitaly Grigoriev
 
Миф об Agile как это работает в реальности / Анатолий Стояновский (ТАСС)
Миф об Agile как это работает в реальности / Анатолий Стояновский (ТАСС)Миф об Agile как это работает в реальности / Анатолий Стояновский (ТАСС)
Миф об Agile как это работает в реальности / Анатолий Стояновский (ТАСС)
Ontico
 

Viewers also liked (20)

Сергей Федоров - Современная безопасность от Eset
Сергей Федоров - Современная безопасность от EsetСергей Федоров - Современная безопасность от Eset
Сергей Федоров - Современная безопасность от Eset
 
SerVal site monitoring presentation - Презентация SerVal
SerVal site monitoring presentation - Презентация SerValSerVal site monitoring presentation - Презентация SerVal
SerVal site monitoring presentation - Презентация SerVal
 
6.2. Hacking most popular websites
6.2. Hacking most popular websites6.2. Hacking most popular websites
6.2. Hacking most popular websites
 
анализ кода: от проверки стиля до автоматического тестирования
анализ кода: от проверки стиля до автоматического тестированияанализ кода: от проверки стиля до автоматического тестирования
анализ кода: от проверки стиля до автоматического тестирования
 
4.3. Rat races conditions
4.3. Rat races conditions4.3. Rat races conditions
4.3. Rat races conditions
 
Intercepter-NG: сниффер нового поколения
Intercepter-NG: сниффер нового поколенияIntercepter-NG: сниффер нового поколения
Intercepter-NG: сниффер нового поколения
 
Как справиться с разочарованием - Dealing with Disappointment
Как справиться с разочарованием - Dealing with DisappointmentКак справиться с разочарованием - Dealing with Disappointment
Как справиться с разочарованием - Dealing with Disappointment
 
Нравственные ценности: Позитивное Мышление – Moral Values: Positive Thinking
Нравственные ценности: Позитивное Мышление – Moral Values: Positive ThinkingНравственные ценности: Позитивное Мышление – Moral Values: Positive Thinking
Нравственные ценности: Позитивное Мышление – Moral Values: Positive Thinking
 
Продвижение сайтов в 2016: нюансы и нововведения
Продвижение сайтов в 2016: нюансы и нововведенияПродвижение сайтов в 2016: нюансы и нововведения
Продвижение сайтов в 2016: нюансы и нововведения
 
Алексей Ионов, Agile на раз-два
Алексей Ионов, Agile на раз-дваАлексей Ионов, Agile на раз-два
Алексей Ионов, Agile на раз-два
 
Сергей Полаженко - Security Testing: SQL Injection
Сергей Полаженко - Security Testing: SQL InjectionСергей Полаженко - Security Testing: SQL Injection
Сергей Полаженко - Security Testing: SQL Injection
 
Идентификация и аутентификация - встроенные функции безопасности или задачи с...
Идентификация и аутентификация - встроенные функции безопасности или задачи с...Идентификация и аутентификация - встроенные функции безопасности или задачи с...
Идентификация и аутентификация - встроенные функции безопасности или задачи с...
 
Непрерывный анализ качества кода с помощью SonarQube
Непрерывный анализ качества кода с помощью SonarQubeНепрерывный анализ качества кода с помощью SonarQube
Непрерывный анализ качества кода с помощью SonarQube
 
Культура Agile
Культура AgileКультура Agile
Культура Agile
 
Presentation at the conference
Presentation at the conferencePresentation at the conference
Presentation at the conference
 
Денис Тучин, Удачные и неудачные паттерны распределённого Agile
Денис Тучин, Удачные и неудачные паттерны распределённого AgileДенис Тучин, Удачные и неудачные паттерны распределённого Agile
Денис Тучин, Удачные и неудачные паттерны распределённого Agile
 
Сергей Рогачев. Agile на гигантских размерах
Сергей Рогачев. Agile на гигантских размерахСергей Рогачев. Agile на гигантских размерах
Сергей Рогачев. Agile на гигантских размерах
 
Кибербезопасность 2016-2017: От итогов к прогнозам
Кибербезопасность 2016-2017: От итогов к прогнозамКибербезопасность 2016-2017: От итогов к прогнозам
Кибербезопасность 2016-2017: От итогов к прогнозам
 
Описание задач «Научного Хакатона»
Описание задач «Научного Хакатона»Описание задач «Научного Хакатона»
Описание задач «Научного Хакатона»
 
Миф об Agile как это работает в реальности / Анатолий Стояновский (ТАСС)
Миф об Agile как это работает в реальности / Анатолий Стояновский (ТАСС)Миф об Agile как это работает в реальности / Анатолий Стояновский (ТАСС)
Миф об Agile как это работает в реальности / Анатолий Стояновский (ТАСС)
 

Similar to PiterPy#3. DSL in Python. How and why?

Курсы по мобильной разработке. 1 лекция. Знакомство с iOS
Курсы по мобильной разработке. 1 лекция. Знакомство с iOSКурсы по мобильной разработке. 1 лекция. Знакомство с iOS
Курсы по мобильной разработке. 1 лекция. Знакомство с iOS
Глеб Тарасов
 
Павел Павлов - Scala для Java программистов (JavaDay Nsk 28.11.2013)
Павел Павлов - Scala для Java программистов (JavaDay Nsk 28.11.2013)Павел Павлов - Scala для Java программистов (JavaDay Nsk 28.11.2013)
Павел Павлов - Scala для Java программистов (JavaDay Nsk 28.11.2013)
ScalaNsk
 
Все, что вы хотели знать о Rebar, но ленились прочитать
Все, что вы хотели знать о Rebar, но ленились прочитатьВсе, что вы хотели знать о Rebar, но ленились прочитать
Все, что вы хотели знать о Rebar, но ленились прочитать
Yuri Zhloba
 
ZFConf 2010: Zend Framework and Doctrine
ZFConf 2010: Zend Framework and DoctrineZFConf 2010: Zend Framework and Doctrine
ZFConf 2010: Zend Framework and Doctrine
ZFConf Conference
 

Similar to PiterPy#3. DSL in Python. How and why? (20)

DSLs in Lisp and Clojure
DSLs in Lisp and ClojureDSLs in Lisp and Clojure
DSLs in Lisp and Clojure
 
Зачем нужна Scala?
Зачем нужна Scala?Зачем нужна Scala?
Зачем нужна Scala?
 
Интеграция Яндекс Сервер
Интеграция Яндекс СерверИнтеграция Яндекс Сервер
Интеграция Яндекс Сервер
 
Go Template Toolkit, Сергей Свистунов, Lazada
Go Template Toolkit, Сергей Свистунов, LazadaGo Template Toolkit, Сергей Свистунов, Lazada
Go Template Toolkit, Сергей Свистунов, Lazada
 
Active Record for CoreData
Active Record for CoreDataActive Record for CoreData
Active Record for CoreData
 
Внешние языки DSL на funcparserlib
Внешние языки DSL на funcparserlibВнешние языки DSL на funcparserlib
Внешние языки DSL на funcparserlib
 
Расширение библиотеки Slick
Расширение библиотеки SlickРасширение библиотеки Slick
Расширение библиотеки Slick
 
msumobi2. Лекция 1
msumobi2. Лекция 1msumobi2. Лекция 1
msumobi2. Лекция 1
 
Anton Shabouta "Implementing async binary clients in pure PHP"
Anton Shabouta "Implementing async binary clients in pure PHP" Anton Shabouta "Implementing async binary clients in pure PHP"
Anton Shabouta "Implementing async binary clients in pure PHP"
 
Курсы по мобильной разработке. 1 лекция. Знакомство с iOS
Курсы по мобильной разработке. 1 лекция. Знакомство с iOSКурсы по мобильной разработке. 1 лекция. Знакомство с iOS
Курсы по мобильной разработке. 1 лекция. Знакомство с iOS
 
Язык программирования Go для Perl-программистов
Язык программирования Go для Perl-программистовЯзык программирования Go для Perl-программистов
Язык программирования Go для Perl-программистов
 
Предметно-ориентированные языки программирования (DSL)
Предметно-ориентированные языки программирования (DSL)Предметно-ориентированные языки программирования (DSL)
Предметно-ориентированные языки программирования (DSL)
 
Павел Павлов - Scala для Java программистов (JavaDay Nsk 28.11.2013)
Павел Павлов - Scala для Java программистов (JavaDay Nsk 28.11.2013)Павел Павлов - Scala для Java программистов (JavaDay Nsk 28.11.2013)
Павел Павлов - Scala для Java программистов (JavaDay Nsk 28.11.2013)
 
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
Руслан Гроховецкий "Как Python стал делать погоду в Яндексе"
 
Все, что вы хотели знать о Rebar, но ленились прочитать
Все, что вы хотели знать о Rebar, но ленились прочитатьВсе, что вы хотели знать о Rebar, но ленились прочитать
Все, что вы хотели знать о Rebar, но ленились прочитать
 
Java8. Innovations
Java8. InnovationsJava8. Innovations
Java8. Innovations
 
Yserver
YserverYserver
Yserver
 
ZFConf 2010: Zend Framework and Doctrine
ZFConf 2010: Zend Framework and DoctrineZFConf 2010: Zend Framework and Doctrine
ZFConf 2010: Zend Framework and Doctrine
 
Back to the future: Функциональное программирование вчера и сегодня
Back to the future: Функциональное программирование вчера и сегодняBack to the future: Функциональное программирование вчера и сегодня
Back to the future: Функциональное программирование вчера и сегодня
 
Algo 00
Algo 00Algo 00
Algo 00
 

PiterPy#3. DSL in Python. How and why?