Pull to refresh

Comments 13

Да. Есть способ конвертировать?

Не уверен. Лично меня скорее язык смутил — невооруженным глазов местами видно, что текст был изначально не на русском )))

Пытался русифицировать, получалось только с потерей смысла.
А зачем нужна вот такая штука:

{type L[T] = Exp[T] => T})#L

когда есть

(Exp[T] => T) forSome {type T}?

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

Типы, которые вы написали, не эквивалентны. Первое — это тип второго порядка, т.е. с сигнатурой * -> *. Второе — это конкретный (первого порядка) экзистенциальный тип, т.е. с сигнатурой просто *. Грубо говоря, первый тип вы можете применить к какому-то типу и получить новый тип, второй же применять не к чему, он уже конкретный, хоть и экзистенциальный. Вот этот плагин к компилятору: https://github.com/non/kind-projector позволяет записывать такие типы удобнее.

Данный тип говорит о том, что значение полиморфно.
Т.е. для любого типа T мы можем получить Exp[T] => T.
Тип (Exp[T] => T) forSome {type T} означает, что существует такой T, для которого мы получили Exp[T] => T. Просто забыли, для какого.
Разница у этих двух определений — в квантификаторе. для любого vs существует
Строго говоря, в scala нет понятия типа полиморфной функции, поэтому эта type lambda просто для понимания проблемы, стоящей перед нами в текущий момент

Чрезвычайно интересно!

А что нужно знать и уметь, чтобы это всё можно было попробовать в деле?

Меня самого интересуют подходы к построению типов данных. (Будут интересовать в будущем. Надеюсь, в очень ближайшем.) Я очень хочу овладеть Хаскелом. Могу ли я сконцентрироваться только на нём (пока), без обращения к Скала и т.п. вещам?

И ещё. Что означает в Вашей статье слово «закодировано»? А то я, от недостатка чего-то (знаний, что ли?) могу что-то в Вашей статье не так понять, и моё воображение рисует мне картину, в которой, вместо железной логики обыкновенных типов данных, «зашитой» в компилятор и непосредственно опирающийся на процессор, имеются какие-то полусимволические представления, которые надо ещё дополнительно интерпретировать во время исполнения (наверное, их можно как-то интерпретировать ещё во время компиляции или ещё на этапе создания кода; я, даже, не знаю, что ещё и думать). Если то, что описываете Вы, как-то похоже на то, что рисуется мне, то, наверное, вполне возможно, будет вести речь о такой штуке. Допустим, мы реализуем систему научных расчётов (типа MATLAB) с участием матриц. Тогда, если мы описываем какой-то матричный алгоритм с участием матричного умножения и операции обращения матрицы, то мы сможем описывать этот алгоритм именно в его обобщённом матричном виде, не опираясь на какую-то реализацию каждой операции, а иметь схему вычислений, в которую можно вставить (в зависимости от контекста) или расширить (в Вашей терминологии?) подходящую реализацию того же умножения и/или обращения.

Другая сторона этого вопроса — это возможность представить сам программный код в виде текста, где, собственно, код и данные оказываются сильно перемешанными и, по сути, самоинтерпретируемыми. Раз так, то, вообще, можно смешать программный и код и текст (на естественном языке), иметь некую единую «кодовую таблицу», а оперативную память воспринимать как определённым образом структурированную базу данных, которую надо единожды создать при загрузке ОС (без необходимости её перераспределять в процессе работы)… В этом смысле, различным традиционным приложениям будут соответствовать наборы расширений для интерпретаторов системных типов. Получается, что ОС может оказаться единым адресным пространством для типов данных, и тогда слово «операционная» следует понимать буквально: как реализацию некоторой системы алгебр, что делает все выполняемые действия: а) проверяемыми; б) обратимыми; и в) доказательными…

Вот, чтобы таких фантазий было поменьше, хотелось бы узнать, что нужно в первую очередь изучить чтобы правильно понимать, то, что нужно понимать правильно.

Статья является фактически переложением лекции Олега Киселёва на Haskell. Поэтому, используя Haskell, вы несомненно сможете опробовать все эти практики и даже с меньшими синтаксическими накладками.


Ваше воображение рисует вам правильную картину. Основная задача — построить какое-то синтаксическое дерево для предметной области, которое можно по-разному интерпретировать в зависимости от ситуации.

Тут дело не совсем в хаскеле (хотя изначально идея скорее всего оттуда). Дело в том, что dotty — это вероятное будущее скалы, которое интересно попробовать.

> А что нужно знать и уметь, чтобы это всё можно было попробовать в деле?

Данная статья исчерпывающая.

> Я очень хочу овладеть Хаскелом. Могу ли я сконцентрироваться только на нём (пока), без обращения к Скала и т.п. вещам?

Да, конечно. Я вообще не понимаю людей, которые из скалы делают хаскель. Ведь есть же уже один. Из хаскеля хаскель получается даже лучший, чем из скалы.

Всё очень просто. Вместо типа данных передаётся полиморфная функция. Тип данных — это стандартное предописанное представление. Функция — это код, который собирает нужную структуру на лету. В функциональных языках вполне себе можно передавать функцию как аргумент. Каждый раз, когда вы обращаетесь к данным, закодированным в функцию, вызывается эта функция и даёт вам ответ.

Можно задать себе вопрос, а зачем такие сложности, зачем передавать функцию, которую ещё нужно исполнить, вместо готового результата? Фишка в том, что функцию полиморфная. В разных контекстах (разных наследниках одного и того же класса), она строит фактически разные структуры. Ну вроде можно как три кирпича в ряд положить, или коржика для тортика. Всё это актуально потому что, голые структуры никому не интересны. Обычно вычисляется какая-то функция от них. Иногда свёртка в строчку или число: toString, toInt. Иногда на базе одной структуры вычисляется другая. Тогда мы можем склеить вычисления структуры-аргумента и функции от неё. В этом случае оверхед на представление структуры как кода, который её генерирует уже не так высок.

Впрочем, если структура нас интересует больше как структура, чем источник вычисления, то не обойтись без её канонического «начального» представления. Например, если мы хотим её сериализовать и передать по сети. Потому как начальное представление — это частный случай сериализации. И тогда уже нет никакой разницы, какое представление использовать. Потому что полифорфизм — это не исключительное свойство конечного представления. Полимформизм бывает и для данных. Открытая рекурсия для данных тоже есть, и конструктор Fix тоже.

Конечное представление — это просто такой фокус, способ переключить внимание. Как паттерн visitor в ООП.

> Допустим, мы реализуем систему научных расчётов (типа MATLAB) с участием матриц.

Тогда не нужен оверхед на конечное представление данных. Используйте просто полиморфные типы без извращений в функциональном стиле.

> Вот, чтобы таких фантазий было поменьше, хотелось бы узнать, что нужно в первую очередь изучить чтобы правильно понимать, то, что нужно понимать правильно.

Прям даже не знаю. Всё так взаимосвязано, а понимание приходит само, от сравнения разных подходов и методик. Чем больше знаешь разных концепций, тем лучше понимаешь одну конкретную в сравнении с остальными. Могу только посоветовать стандартный университетский курс.
Из хаскеля хаскель получается даже лучший, чем из скалы.

Ну это как раз просто понять. Scala — это jvm. Это далеко не только язык, и хаскель его не заменяет и вероятно никогда полностью не заменит.


Хотя в контексте ответа на конкретный вопрос вы несомненно правы, если хочется овладеть хаскелем, нужно овладевать хаскелем. Если хочется овладеть ФП — то вполне можно и скалой. Практических применений возможно будет и больше (с учетом спарка и т.п.).

"Закодировано" это encoding, и у него в английском несколько другой основной смысл, нежели получается в русском. И ваше понимание вполне близко к истине. Речь по сути идет от широко известной expression problem, и варианте ее решения.

Sign up to leave a comment.

Articles