?

Log in

No account? Create an account
Не только лишь все знают, что в Ocaml'е параметры функций
вычисляются немножко по-арабски - справа налево. И это суровая
оптимизационная необходимость, описанная в книге Ксавье Леруа
"The ZINC experiment: an economical implementation of the ML language",
которую можно скачать у него на сайте https://xavierleroy.org/bibrefs/Leroy-ZINC.html,
(страница 14).

То есть, если мы загрузим REPL и напишем

(fun _ _ -> ()) (print_string "Left") (print_string "Right")

мы получим "RightLeft". Ничего вроде бы интересного - что заказывали,
то и получили.

Теперь вспомним, что в ML в 80-х происходил переход от кортежей
к каррированным функциям. То есть, вместо f (x, y) люди начали писать
f x y (в библиотеке SML Basis ещё остались следы древних времён).
То есть, немного предсказуемо, из

(fun _ -> ()) (print_string "Left", print_string "Right")

мы получаем опять "RightLeft". Вроде бы так и должно быть - каррированная
функция f x y могла быть записана программистом как f(x,y), а значит оба
варианта должны иметь одинаковые побочные эффекты, чтобы сюрпризов было
по-меньше.

Тем не менее, (x,y) - это кортеж. Соответственно, порядок инициализации полей
кортежа (a, b, c, d) - справа налево. Для проверки

(print_string "Left", print_string "Middle", print_string "Right")

в REPL действительно даёт "RightMiddleLeft".

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

[ print_string "Left"; print_string "Middle"; print_string "Right"]

и

[| print_string "Left"; print_string "Middle"; print_string "Right"|]

убеждаемся, что и списки, и массивы инициализируются по-арабски, справа налево.
Аналогично работает инициализация полей структур:

type r = { a : unit; b : unit; c : unit }
let _ = { a = print_string "Left"; b = print_string "Middle"; c = print_string "Right" }

И, что самое поразительное, точно также работают и функторы - программа

module type S = sig val x : unit end

module F : functor (X : S) (Y : S) -> struct end

module C = F (struct let x = print_string "Left" end)
(struct let x = print_string "Right" end)

выводит "RightLeft". С объектами дела обстоят точно также - new obj a b вычислит сперва b, затем a.

И вроде бы всё безумно одинаково, последовательно, скучно и совершенно нехарактерно
для эклектичного Ocaml, но идиллию портят

(print_string "Left"; print_string "Middle"; print_string "Right")

выводящее "LeftMiddleRight", как и положено блоку последовательных команд, да

{|print_string "Left"; print_string "Middle"; print_string "Right"|}

которое вообще строка. И, всё-таки, это Ocaml!

Ocaml типы

В Ocaml'е, как известно, конструкторы значений не являются функциями.
Сделано это по ряду соображений, которые Ксавье Леруа изложил в
комментарии http://caml.inria.fr/pub/ml-archives/caml-list/2001/08/47db53a4b42529708647c9e81183598b.fr.html

Вкраце - это не имеет никакого отношения к производительности, а
обусловлено исторически-стилистическими соображениями и небольшим упрощением
компилятора.

Синтаксис декларации типа определён ещё в статье Милнера от 1978-го года
(до рождения половины посетителей куздрятника), и с тех пор он почти не
изменился. Милнер ссылался на статью Хиндли, которая ещё на десяток лет
старше, и, видимо, старался использовать сходные обозначения. В частности,
как и Хиндли, он использовал перечисления параметров через запятую.
Кстати, оттуда же идёт произношение 'a как "альфа", а 'b как "бета"
(к сожалению, я так до сих пор и не знаю, как произносится 'c).

В статье Милнера первой же формулой идёт тип полиморфной функции map:

(('a -> 'b) * 'a list) -> 'b)

И с тех пор у нас в языках семейства ML идёт запись конструктора
типов с небольшим арабским акцентом - слева направо и обязательно в скобочках.
В основном языке всё наоборот - имя функции слева, а параметры через пробел справа:

type ('a, 'b, 'c) triad = ...

let triad' a b c = ...

В языках с синтаксисом Mirand'ы это всё, разумеется, исправили, но Ocaml
несёт груз традиции.

А вот более неприятно то, что конструктор значения в синтаксисе
ML имеет ровно один параметр. То есть, мы можем написать

type 'a pair = Pair of 'a*'a

но не можем написать

type 'a pair = Pair of 'a 'a

С другой стороны, внутри компилятора Ocaml кортеж 'a*'a естественным образом
разбивается на два параметра. Это непонятное место, где реализация
противоречит языку и было одним из мотиваторов сделать конструкторы
значений не-функциями. Кстати говоря, в более традиционном SML
у очень многих функций параметры сгруппированы в пары;
это тепло и лампово, но крайне неудобно.
Волею судеб я сейчас изучаю язык Ocaml, вернее убираю пробелы.
Ocaml - древний язык, если учитывать его родословную, поэтому
в нём можно легко найти множество костылей, некрасивостей,
и откровенно слабых мест.

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

Первое, это, конечно, примотанная изолентой и соплями система модулей.
Рассмотрим простейшие конструкции - это пустой модуль и пустой тип модуля:

module X = struct end

module type Z = sig end

Сразу же бросаются в глаза явно инородные ключевые слова struct и sig, кроме того,
ключевое слово 'end' нельзя заменить на ')' как в основном языке да и у
него нет пары begin.

Сокращения struct и sig произошли от structure/signature в SML (MacQueen, D B (1984). Modules for Standard ML.):

structure X = struct end

signature Z = sig end

Для ядра SML же обычен и непарный end - "let x = 8 in x end".

Хотя описание и определение функтора в Ocaml выглядят похоже на синтаксис
основного языка, хотя и содержат module вместо let:

module X = functor (A : Asig) (B : Bsig) -> struct end

Конечно, система модулей примотана изолентой и в SML, но там она
выглядит хоть чуточку более целостной. И, поскольку сам язык SML
многословнее, чем CAML, подъязык модулей выглядит в нём достаточно
естественно.

В F#, кстати, этот синтаксис заменён на более лаконичный.

Tags:

Не только лишь все знают, что обычный Хаскельный пример quicksort'а несколько жульнический. Тем не менее, можно сделать реальный in-place quicksort на изменяемых массивах. Ниже - программа на Clean:

module quicksort
import StdEnv
import StdClass

swap :: !*(a e) !Int !Int-> *(a e) | Array a e
swap arr i j
    | i == j = arr
    | i <> j = arr```
    where (a_i, arr`)   = uselect arr i
          (a_j, arr``)  = replace arr`  j a_i
          (_, arr```)   = replace arr`` i a_j

partition :: !*(a e) !Int !Int -> (Int, *(a e)) | Array a e & Ord e
partition arr lo hi = go (uselect arr hi) lo lo
    where go (pivot, arr) i j
            | j >= hi   = (i, swap arr i hi)
            | otherwise = if (a_j < pivot)
                            (go (pivot, swap arr` i j) (i + 1) (j + 1))
                            (go (pivot, arr`) i (j + 1))
                          where (a_j, arr`) = uselect arr j

quicksort :: !*(a e) !Int !Int -> *(a e) | Array a e & Ord e
quicksort arr lo hi
            | lo >= hi  = arr
            | otherwise = quicksort arr`` p hi
                          where arr``     = quicksort arr` lo (p - 1)
                                (p, arr`) = partition arr  lo hi

Start :: *{#Real}
Start = quicksort {1.0, 2.0, 4.0, 3.0, 0.0} 0 4


По итогам эксперимента выяснилось, что писать на unique types крайне неудобно. Библиотека совершенно не продумана в плане работы с массивами - мне пришлось вводить даже функцию swap. Явно не хватает аналогов VectorSlice из SML.

С другой стороны, размер кода примерно такой же, как и на императивном псевдокоде, при том, что явно можно ещё подсократить код, если ввести оператор применения $ и поиграться с порядоком аргументов функций.

Ну и есть предложение переписать программу на линейных типах Хаскеля.

Достаточно хорошо структурированный список школьных учебников времён СССР - http://fremus.narod.ru/schoolbk.html.

Разумеется, представлены не все, но тем не менее, их достаточно много. Очень приятно то, что выписаны не просто названия учебников, а даны даты и обложки. К сожалению, отсканирована лишь часть учебников. Кроме того, сканы не прошли через программы ScanTailor/ScanKromsator, поэтому не пригодны для распечатки.
Покопавшись в том, что сейчас можно скачать отсканированного из школьных учебников, нашёл курс арифметики авторов Пчелко А.С., Поляк Г.Б. В поисковике можно найти все четыре тома курса по одному для каждого из 4-х классов начальной школы. Называются они однотипно "Арифметика для № класса".

Начало первого класса проще всего объяснять на материальных предметах - бусинах, кубиках лего и т.д. Дальше можно переходить на рисунки. Задач и примеров в курсе много, если решать их последовательно, ребёнок всё воспримет.

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

Основное достоинство этих книг, на мой взгляд, в том, что в отличие от гимназических учебников на бумаге не экономили, курс полный и последовательный. С другой стороны, задачи не очень сложные, хотя можно ведь и вперёд пройти.
Начну, как обычно начинают, с формата djvu. Это формат изображений, специально разработанный для сохранения отсканированного текста и штрихованных рисунков с очень хорошей степенью сжатия. По сравнению с pdf, в котором рисунки кодируются в jpeg'е этот формат даёт очень качественные и маленькие по объёму изображения.

С появлением djvu народ начал массово сканировать научно-техническую литературу, оставшуюся нам от СССР. В результате появилась библиотека КОЛХОЗА, а потом подтянулись и другие. Средства оцифровки и чистки сканов также развивались. Вначале пользовались нелицензионным BookRestorer, затем появились BRTS tools, потом наш глубокоуважаемый соотечественник Bolega начал свою работу над замечательной программой ScanKromsator. И, относительно недавно, появился открытый ScanTailor, опять-таки написанный другим нашим замечательным соотечественником Иосифом Арцимовичем (возможно, псевдоним).

В результате, на данный момент оцифровано уже очень много книг, выпущенных в СССР, созданы превосходные инструменты для создания высококачественнейших электронных отсканированных научно-технических книг.

И сейчас встаёт вопрос - что с этим делать? Нет, конечно, есть превосходные каталоги, например, у той же библиотеки КОЛХОЗА. Но, неужели пыльная библиотека - это единственное, что можно получить из наследия СССР?

Мне кажется, что нет, не только. Сейчас, как мы знаем, период упадка, тяжёлой болезни страны и образования. Поэтому многое приходится проходить с детьми дома. А для этого нужны полные КУРСЫ. То есть, курс литературы, курс математики, курс физики.

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