LINUX.ORG.RU

Parrot, или Почему мы долго ожидаем Perl 6


0

0

"Как известно, впервые Perl был представлен на суд общественности Ларри Уоллом (Larry Wall) еще в конце 1987 г. В последующие несколько лет язык бурно развивался, одна за другой выходили новые версии, и уже в 1994 г. программисты работали с Perl 5. Однако с тех пор прошло тринадцать лет, и хотя за это время появлялись очередные релизы, номер текущей версии по-прежнему начинается с пятерки. Значит ли это, что Perl идеален? Спору нет, он очень хорош, но и предела совершенствованию, как известно, нет. А одна из причин задержки шестой версии состоит в том, что в ней ожидается поистине революционное новшество - переход на новую систему промежуточного представления кода."

>>> Статья на itc.ua



Проверено: maxcom ()

Ответ на: комментарий от tailgunner

> О, конкретный разговор пошел. Пара вопросов: так ты признаешь, что 
это всё-таки closure? чем/для чего тебе это не подходит?

Конечно признаю. Но мне нужно не просто замыкание внутри функции, а 
анонимная функция, которая имеет еще свойство closure. Собственно так 
оно работает в руби и хаскелле. Например в пхп есть анонимная 
функция, но она не closure и потому не юзабельна. В питоне есть 
closure, но он не поверх анонимной функции и поэтому тоже неюзабелен.

Приведу пример:

file_name = "test.txt"
Dir.chdir ("/home/anonymous/") {
  text = File.open(file_name) {|file_handler| file_handler.read}
  puts text
}


В этом примере методу chdir передается анонимная_функция+closure 
{text = File.open(file_name).read} . В данном случае без аргументов. 
Эта анонимная функция "захватывает" переменную file_name и может прочитать ее содержимое.

Метод chdir делает переход в /home/anonymous выполняет блок 
кода(yield) и возвращается назад в предыдущаю папку:

def Dir.chdir(new_dir)
  old_dir = Dir.pwd
  __chdir(new_dir)
  yield
  __chdir(old_dir)
end

Я написал примерную реализацию, просто чтобы показать как это работает.
Такой же подход используется для открытия/закрытия файлов, базы данных и т.п.

Тоесть код File.open(file_name) {|file_handler| file_handler.read}
открывает файл на чтение. Выполняет блок кода file_handler.read и
закрывает файл. Здесь блок принимает один аргумент 
file_handler(назвал так для ясности). 

В Питоне для реализации данной фичи пришлось вводить with. Хотя в других
языках это делается на уровне анонимн.фции+closure. 

Cris
()
Ответ на: комментарий от Cris

> В питоне есть closure, но он не поверх анонимной функции и поэтому тоже неюзабелен.

Мне много раз это говорили, но никто не убедил. "Неюзабельно" - это значит, что наличие у функции имени мешает ей выполнять свою работу? Очевидно же, что это не так. В варианте с неанонимной функцией текста на 2 строки больше, это что, решающий аргумент?

> Я написал примерную реализацию, просто чтобы показать как это работает. Такой же подход используется для открытия/закрытия файлов, базы данных и т.п.

То, что красиво выглядит на небольшом "учебном" примере, не обязательно выглядит так в Реальной Жизни (r) (tm). А функцию, которая возвращает "обертку" для другой функции, можно сделать и в Питоне.

> В Питоне для реализации данной фичи пришлось вводить with.

Насколько я знаю, with в Python 2.5 - это скорее техника RAAI. С дополнительным бонусом - специальная языковая конструкция сразу дает понять: "здесь юзается RAAI".

tailgunner ★★★★★
()
Ответ на: комментарий от eugine_kosenko

>> там даже кложуры есть

> Типа через Ж сделанные.

Отож. Ну а у тебя какие претензии к кложурам Питона?

> А коммьюнити в очередной раз ждет ебилдов...

Не понял... вы там день независимости России от СССР праздунете? :D

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Ну а у тебя какие претензии к кложурам Питона?

Да никаких кроме сугубо эстетических. Хотя не исключено, что я просто не травмирован более сложными случаями...

> Не понял... вы там день независимости России от СССР праздунете?

Просто нормальные замыкания пообещали сделать в светлом будущем. Уже не помню точно, то ли в 2.5, то ли в 3000.

eugine_kosenko ★★★
()
Ответ на: комментарий от tailgunner

> То, что красиво выглядит на небольшом "учебном" примере, не обязательно выглядит так в Реальной Жизни (r) (tm). А функцию, которая возвращает "обертку" для другой функции, можно сделать и в Питоне.

Для начала напиши реализацию на Питоне с использование closure, потом продолжим разговор.

Кстати, переход по каталогам и открытие+закрытие файлов и баз данных, это отличный пример из Реальной Жизни (r) (tm).

Еще один момент. Это простые примеры. Есть более интересные, в которых и содержится суть DSL-лей Руби. Но писать дальше не буду, пока не напишешь реализацию на Питоне chdir и open.

Cris
()
Ответ на: комментарий от tailgunner

Задача заключается в том, чтобы написать функцию, которая будучи
вызвана с параметром, возвращает замыкание, которое при каждом
следующем вызове возвращает значение на единицу большее, чем
предыдущее, а самое первое значение определяется параметром
генератора. То есть, должно быть нечто следующее:

>>> f1=g(5)
>>> f2=g(10)
>>> f1()
5
>>> f2()
10
>>> f1()
6
>>> f1()
7
>>> f2()
11
>>> f2()
12

и т.д.

Ваше решение?

eugine_kosenko ★★★
()
Ответ на: комментарий от eugine_kosenko

> Задача заключается в том, чтобы

Ну вот ни слова в простоте. Так бы и сказал - мое эстетическое чувство оскорбляет то, что я не могу присваивать значения переменным в объемлющей области видимости. Ну и решение ты знаешь, конечно же...

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Так бы и сказал - мое эстетическое чувство оскорбляет то, что я не могу присваивать значения переменным в объемлющей области видимости.

Нет, не так. Присваивать, как оказалось, могу, но через Ж. Об что и написал.

> Ну и решение ты знаешь, конечно же...

Конечно же знаю, умные люди уже подсказали. Ждем еби^W Python-3000. Хотя особо продвинутые говорят, что в svn уже есть...

eugine_kosenko ★★★
()
Ответ на: комментарий от Cris

> Для начала напиши реализацию на Питоне с использование closure, потом продолжим разговор

Суров, да. Такая подойдет, или closure надо использовать как-то заковыристее?

#!/usr/bin/python

import sys, os

def cd(newdir, f):
        olddir = os.getcwd()
        os.chdir(newdir)
        f()
        os.chdir(olddir)

fname = "file.txt"
def handler():
        for l in file(fname).readlines():
                sys.stdout.write(l)
cd("/home/anonymous", handler)

Более приближенно к Реальной Жизни (r) (tm), я бы написал так:

def handler(fname):
        for l in file(fname).readlines():
                sys.stdout.write(l)
cd("/home/anonymous", lambda: handler("file.txt"))

Ну что, достоин я продолжения разговора? :D

tailgunner ★★★★★
()
Ответ на: комментарий от eugine_kosenko

> Присваивать, как оказалось, могу, но через Ж

Хм, именно присваивать, оператором "="? Именно переменным в объемлющей области видимости, а не их полям или элементам?

tailgunner ★★★★★
()
Ответ на: комментарий от mihalych

> ненароком за конец блока перейдёшь и лишнюю строку переформатируешь.

А что будет, если ненароком за конец блока перейдешь и закрывающую скобку после лишней строчки поставишь? Как страшно жить!

> Ошибиться легко, ведь у блока нет никаких видимых ограничителей.

Особенно, когда ССЗБ и не ставишь вокруг блока прекрасно видимые пустые строки. Пустые строки, надеюсь, еще различаем?

> В Питон мало того, что лишняя индентация полностью меняют смысл кода, так ещё и полностью теряется этот самый оригинальный смысл и его невозможно никак востановить. Хоть убейся, хоть полный undo делай и заново всё переформатировать начинай.

А в Перле лишняя закрывающая скобка делает код полностью неработоспособным. Хоть убейся, хоть полный undo делай и заново ищи, куда эту скобку перенести.

> Да не решают они ничего. Это в принципе нерешаемая задача.

Так и запишем: нормальных редакторов ниасилил.

> Убрал пробел или добавил - остался верный синтакс, хоть и неверный код.

Вы меня поражаете своей глубиной знаний синтаксиса Питона :-). Ладно, скажу по секрету, что после "убрал пробел или добавил" Питон отваливается с синтаксической ошибкой точно так же, как Перл делает это от "убрал скобку или добавил".

> И странно, что все любители питона советуют сменить текстовый редактор.

Открою Вам страшную тайну: все любители Лиспа тоже советуют это сделать. Догадываетесь, почему? ;-)

> Может сразу и ОС поменять?

Ну, если мсье предпочитает пользоваться старым добрым notepad, то зачем же себя насиловать?

eugine_kosenko ★★★
()
Ответ на: комментарий от tailgunner

> Хм, именно присваивать, оператором "="?

Именно так.

> Именно переменным в объемлющей области видимости, а не их полям или элементам?

Ну, если бы еще и полям/элементам было нельзя, то мои претензии явно бы вышли за рамки "сугубо эстетических" :-)

eugine_kosenko ★★★
()
Ответ на: комментарий от eugine_kosenko

> Задача заключается в том, чтобы написать функцию, которая будучи
вызвана с параметром, возвращает замыкание, которое при каждом
следующем вызове возвращает значение на единицу большее, чем
предыдущее, а самое первое значение определяется параметром
генератора.


In [67]: def counter(start):
   ....:     curr = [start]
   ....:     def inc():
   ....:         curr[0] += 1
   ....:         return curr[0]
   ....:     return inc
   ....:

In [68]: f1 = counter(5)

In [69]: f2 = counter(10)

In [70]: f1()
Out[70]: 6

In [71]: f2()
Out[71]: 11

In [72]: f1()
Out[72]: 7

In [73]: f1()
Out[73]: 8

In [74]: f2()
Out[74]: 12

anonymous
()
Ответ на: комментарий от anonymous

Зачет, и вот тут коронный вопрос -- почему нельзя обойтись скалярами? Альтернативную формулировку этого вопроса уже знаю, и даже ответ знаю. Но в конечном итоге правильный ответ "Ждем Python-3000".

:-)

eugine_kosenko ★★★
()
Ответ на: комментарий от anonymous

> В общем при администрировании перл незаменим, bash сливает по-полной.

> Вот так оно выглядит на перле в одну строчку: perl -le 'foreach(<*>){ -l && !-e && print}'

$ symlinks -v .|grep dangling

Ы?

eugine_kosenko ★★★
()
Ответ на: комментарий от eugine_kosenko

>> Вот так оно выглядит на перле в одну строчку: perl -le 'foreach(<*>){ -l && !-e && print}'

> $ symlinks -v .|grep dangling

> Ы?

Это было неспортивно!

tailgunner ★★★★★
()
Ответ на: комментарий от Cris

> Из всех перечисленных языков питон самый скучный. Он не плох и не хорош. Он скучен. Мало новизны. Когда игрался с питоном особого азарта не было.

Из всех перечисленных инструментов молоток самый скучный. Он не плох и не хорош. Он скучен. Мало новизны. Когда игрался с молотком особого азарта не было.

eugine_kosenko ★★★
()
Ответ на: комментарий от tailgunner

> Это было неспортивно!

А нефик говорить, что шелл сливает...

eugine_kosenko ★★★
()
Ответ на: комментарий от tailgunner

===============
fname = "file.txt"
def handler():
        for l in file(fname).readlines():
                sys.stdout.write(l)
cd("/home/anonymous", handler)
===============
Отличный код, только малоюзабельный. Много головой нужно крутить, чтобы
понять что к чему. Придумывать постоянно название для функции, которая
каждый раз будет новая. Если в скрипте написать 10 cd - 10 новых имен...

А теперь пофантазируем, что Гвидо осенило:

fname = "file.txt"
cd("/home/anonymous") ->
    for l in file(fname).readlines():
        sys.stdout.write(l)

Удобно, понятно и сразу генераторы с with можно выкинуть на мусорку.
А closure из невостребованной хрени превращается в нужный инструмент.

Кроме того, открывается возможность делать кучу конструкций с одним
уровнем вложенности(аля if без else, for, map, select и т.п.).

Cris
()
Ответ на: комментарий от Cris

> Отличный код

/me расшаркивается

> только малоюзабельный.

Упс...

> Много головой нужно крутить, чтобы понять что к чему. Придумывать постоянно название для функции, которая каждый раз будет новая.

Из понятных претензий - только неообходимость придумывать имена. Не очень веская претензия, ИМХО.

> Если в скрипте написать 10 cd - 10 новых имен...

Если тебе в одной функции нужно 10 нетривиальных лямбд - ты что-то делаешь не так.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

>> Если в скрипте написать 10 cd - 10 новых имен...

> Если тебе в одной функции нужно 10 нетривиальных лямбд - ты что-то делаешь не так.

cd - делает cd. open - открывает файл. Если мой скрипт работает с пятью файлами и открывает пять каталогов (понятно, что действия при этом выполняются разные), то придется клепать 10 новых имен.

Второй момент удобство. твой вариант cd и open не входит в стандартную библиотеку. Поэтому придется тягать за собой свою библиотеку, что некритично для больших проектов, но напряжно для небольшого скриптования.

Третье - то, что вместо одной структуры, она нарезана на два куска. Представь себе, что ты описываешь операции для цикла for отдельно, а потом ниже вызываешь его.

Четвертое - введение дополнительных переменных увеличивает количество вероятных ошибок (с) функциональные языки.

Cris
()
Ответ на: комментарий от Cris

> Если мой скрипт работает с пятью файлами и открывает пять каталогов (понятно, что действия при этом выполняются разные), то придется клепать 10 новых имен.

Откуда 10? 5 - еще понятно, но 10? Насчет придумывания имен - считай, что это небольшой комментарий на тему назначения функции.

> твой вариант cd и open не входит в стандартную библиотеку.

open? какой такой open? :) ну и сравниваем мы языки, а не их стандартные библиотеки.

> вместо одной структуры, она нарезана на два куска.

Это в любом случае 2 куска, логически. И в твоем варианте, и в моем эти куски достаточно близко, чтобы увидеть связь.

> введение дополнительных переменных увеличивает количество вероятных ошибок

Я не вижу дополнительных переменных, имя функции - это практически константа, а больше в моем втором варианте просто ничего нет.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Откуда 10? 5 - еще понятно, но 10? Насчет придумывания имен - считай, что это небольшой комментарий на тему назначения функции.

Протупил.

> open? какой такой open? :) ну и сравниваем мы языки, а не их стандартные библиотеки.

Спрошу так. Ты бы пользовался такой конструкцией для себя? Ну вот садишься писать скрипт и вызываешь свой вариант cd? Думаю что нет. Потому что она элементарно __неудобна__.

А я регулярно пользуюсь потому что это __удобно__ в Руби.

Разницу чуствуешь? У тебя closure есть, но применять их тебе неудобно. Поэтому в Питоне они используются в очень редких ситуациях. В Руби они удобны. Поэтому они используются повсеместно. Поэтому на Руби пишут DSL-и, а на Питоне нет.

Cris
()
Ответ на: комментарий от Cris

> Ты бы пользовался такой конструкцией для себя?

Возможно. У меня задачи несколько другие... я довольно часто пользуюсь lambda.

> элементарно __неудобна__

Я бы сказал, что Ruby-вариант _более_ удобен.

> У тебя closure есть, но применять их тебе неудобно. Поэтому в Питоне они используются в очень редких ситуациях.

А еще есть различия в дизайне языков, предпочтениях в профессинальной культуре и много всего. Если бы весь мир считал, что анонимная лямбда - это самый лучший инструмент для всего, мы бы все писАли на Лиспе.

> Поэтому на Руби пишут DSL-и, а на Питоне нет.

Стряхни словесную шелуху и рекламные ярлыки, и ты увидишь, что DSL - это библиотека. А библиотек для Питона написано куда больше, чем для Ruby.

tailgunner ★★★★★
()
Ответ на: комментарий от Cris

Почему рубевские блоки зло? 
Потому что нарушается структурированность императивного кода. 
Проще тупо разделить код на две части:

import os

file_name = 'test.txt'
basedir = os.getcwdu()
os.chdir('/home/anonymous')
print open(file_name,'rt').read()
os.chdir(base)

Почему замыкания зло? Замыкание - суть объединение кода и данных.
А для этого уже есть объекты, посему необходимо использовать их.

yk4ever
()
Ответ на: комментарий от yk4ever

> basedir = os.getcwdu() > os.chdir('/home/anonymous') > print open(file_name,'rt').read() > os.chdir(base)

Тебе в китай, там любят трудолюбивих. Dir.chdir('....') {} сокращает на две строчки размер твоего императивного кода. При частом использовании у тебя та же структура будет разбросана по всему коду.

Cris
()
Ответ на: комментарий от yk4ever

> Почему замыкания зло? Замыкание - суть объединение кода и данных. А для этого уже есть объекты, посему необходимо использовать их.

Почему объекты зло? Объекты - суть объединение кода и данных. А для этого уже есть замыкания, посему необходимо использовать их.

Логично?

eugine_kosenko ★★★
()
Ответ на: комментарий от tailgunner

> DSL - это библиотека

Примеры DSL в Руби. ActiveRecord(модель в рельсах) - да, можно сказать библиотека. Rake - аналог make - нифига не библиотека. Rspec - BDD(продолжение идеи TDD) для руби - нифига не библиотека. Миграции в рельсах - нифига не библиотека.

Из того что помню соотношение 1 к 4. Кстати, посмотри Rspec http://rspec.rubyforge.org/ - симпатично выглядит и по сути единственная живая реализация подхода BDD в программировании.

Cris
()
Ответ на: комментарий от Cris

> У тебя closure есть, но применять их тебе неудобно. Поэтому в Питоне они используются в очень редких ситуациях. В Руби они удобны. Поэтому они используются повсеместно. Поэтому на Руби пишут DSL-и, а на Питоне нет.

А каким боком DSL к замыканиям? ЕМНИП, DSL -- это из области возможного изменения синтаксиса языка. Насколько я помню, ни Руби ни Питон и близко здесь не стояли...

eugine_kosenko ★★★
()
Ответ на: комментарий от yk4ever

Ты мне кстати, еще ссылку на карри не привел. Или умное слово услышал и не по делу его кинул?

Cris
()
Ответ на: комментарий от eugine_kosenko

> А каким боком DSL к замыканиям? ЕМНИП, DSL -- это из области возможного изменения синтаксиса языка. Насколько я помню, ни Руби ни Питон и близко здесь не стояли...

Скорее создание специализированного синтаксиса под задачу. В Руби это реализуется с помощью анонимных замыканий(ака блоков).

http://rspec.rubyforge.org/ - один из лучших примеров, притом активно развивается и используется для тестирования рельс. Легко реализуется на Лиспе и SmallTalk.

Cris
()
Ответ на: комментарий от Cris

> Rake - аналог make - нифига не библиотека. Rspec - BDD(продолжение идеи TDD) для руби - нифига не библиотека.

У нас с тобой сильно разные понятия о том, то такое библиотека :)

> Миграции в рельсах

Об этом ничего не знаю.

> Кстати, посмотри Rspec http://rspec.rubyforge.org/

Смотрел. Я сам когда-то чуть не начал использовать Ruby (но выбрал Питон), товарищ у меня любитель Ruby - так что я более-менее в курсе. RSpec, кстати, меня не впечатлил вообще никак.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

>> Миграции в рельсах

> Oб этом ничего не знаю.

http://garrettsnider.backpackit.com/pub/367902

> Смотрел. Я сам когда-то чуть не начал использовать Ruby (но выбрал Питон), товарищ у меня любитель Ruby - так что я более-менее в курсе. RSpec, кстати, меня не впечатлил вообще никак.

Проникся, когда начал использовать.

Cris
()
Ответ на: комментарий от Cris

> Скорее создание специализированного синтаксиса под задачу. В Руби это реализуется с помощью анонимных замыканий(ака блоков).

Вы не умничайте, Вы задачу Бугмакера вначале реализуйте. Ну и ту самую, из ЛОР-контеста...

eugine_kosenko ★★★
()
Ответ на: комментарий от Cris

> Ну и учитывая, то что ты зашарил Лисп, можешь посмотреть здесь: http://www.randomhacks.net/articles/2005/12/03/why-ruby-is-an-acceptable-lisp

http://www.norvig.com/python-lisp.html

И что, будем дальше меряться пипис^W родством с канониками?

eugine_kosenko ★★★
()
Ответ на: комментарий от tailgunner

> Это про поиск дублирующихся файлов из "Фразы о Лиспе"?

Не, это о реализации синтаксического примитива ssalc. :-)

eugine_kosenko ★★★
()
Ответ на: комментарий от eugine_kosenko

>> Скорее создание специализированного синтаксиса под задачу. В Руби это реализуется с помощью анонимных замыканий(ака блоков).

> Вы не умничайте, Вы задачу Бугмакера вначале реализуйте.

Где ты умничание видишь? Тебе дается нормальный ответ на поставленный вопрос. В статье http://www.randomhacks.net/articles/2005/12/03/why-ruby-is-an-acceptable-lisp есть параграф с названием "Ruby gives you about 80% of what you want from macros", там приводится пример, правда на основе мета-программирования. На практике чаще используются блоки.

Cris
()
Ответ на: комментарий от Cris

> В статье http://www.randomhacks.net/articles/2005/12/03/why-ruby-is-an-acceptable-lisp есть параграф с названием "Ruby gives you about 80% of what you want from macros", там приводится пример, правда на основе мета-программирования.

Ага, на заборе написано сам знаешь что, а лежат там дрова. "About 80%" звучит как нечто вроде "немного беременная" или "макросы второй свежести".

> На практике чаще используются блоки.

Перефразируя классиков:

"У тебя макросы есть, но применять их тебе неудобно. Поэтому в Руби они используются в очень редких ситуациях. В Лиспе они удобны. Поэтому они используются повсеместно. Поэтому на Лиспе пишут DSL-и, а на Руби нет."

(C) явно не мое.

eugine_kosenko ★★★
()
Ответ на: комментарий от eugine_kosenko

> "У тебя макросы есть, но применять их тебе неудобно. Поэтому в Руби они используются в очень редких ситуациях. В Лиспе они удобны. Поэтому они используются повсеместно. Поэтому на Лиспе пишут DSL-и, а на Руби нет."

Мне не трудно признать, что Лисп и Тикль в плане DSL на голову выше Руби. Собственно я сам натыкался на его ограничения.

Но тебе и tailgunner'у трудно признать что Гвидо налажал с лямбдами. Выше были ссылки на симпатичные рубовские DSL-и и другие примеры реального применения, коих нет в Питоне из-за отсутствия лямбды(как вариант добавить хаскельную стрелочку и скобочки для аргументов \ (x,y) -> ).

Или может признаете? :)))))

Cris
()
Ответ на: комментарий от eugine_kosenko

> Так и запишем: вьюношеский максимализм.

http://aroks.com.ua/ -

Warning: rename() [function.rename]: Unable to access /home/arokskie/domains/aroks.com.ua/public_html/stat/data/referers.txt.66704 in /home/arokskie/domains/aroks.com.ua/public_html/stat/func.php on line 41

Warning: unlink() [function.unlink]: Unable to access /home/arokskie/domains/aroks.com.ua/public_html/stat/data/referers.txt in /home/arokskie/domains/aroks.com.ua/public_html/stat/func.php on line 43

Warning: rename() [function.rename]: Unable to access /home/arokskie/domains/aroks.com.ua/public_html/stat/data/referers.txt.66704 in /home/arokskie/domains/aroks.com.ua/public_html/stat/func.php on line 44

Cris
()
Ответ на: комментарий от Cris

> Но тебе и tailgunner'у трудно признать что Гвидо налажал с лямбдами.

Кому, мне трудно? Да как два байта переслать: ГВИДО НАЛАЖАЛ С ЛЯМБДАМИ! Теперь нам нечем ответить на "у вас нет многострочных лямбд"... ну как после этого флеймить в форумах? :(

tailgunner ★★★★★
()
Ответ на: комментарий от Cris

> Поэтому на Руби пишут DSL-и, а на Питоне нет.

What a BULLSHIT!

http://www.livelogix.net/logix/ http://elixir.ematia.de/ http://jjinux.blogspot.com/2007/02/pycon-embedding-little-languages-in.html http://209.85.135.104/search?q=cache:5qxwIrRE9SAJ:groovie.org/articles/2005/12/0 8/is-rails-a-dsl-what-is-a-dsl-and-is-it-possible-in-python+dsl+python&hl=en &ct=clnk&cd=5 etc...

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.