суббота, 28 октября 2017 г.

Ссылка. Erlang

http://learnyousomeerlang.com/content

пятница, 27 октября 2017 г.

Вопрос. Haskell

По итогам:
http://programmingmindstream.blogspot.ru/2017/10/haskell-vs-count.html?m=1

А как length/count (количество элементов списка) на Haskell реализовано?

Так:

Count [] = 0
Count ( _ : xs ) = 1 + Count xs

?

Или можно как-то эффективнее?

четверг, 26 октября 2017 г.

Haskell vs мои скрипты. Count & Map

Integer def List:Count
  List in aList
 aList Match (
  List:[]
   0
  ( List:Make: _ Tail )
   ( 1 + Tail call.me )
 ) // aList Match
; // List:Count

// что тут делать с "длинной рекурсией"
// - пока не очень понятно,
// возможно стоит сделать Count
// "хранимым членом"

Тогда так:

Integer def List:Count
  List in aList
 aList Match (
  List:[]
   0
  ( List:Make: _ Tail Count )
   (
    If ( Count .IsValid ) then
     Count
    Else
     ( 1 + Tail call.me )
   )
 ) // aList Match
; // List:Count

List def List:Map
  List in aList
  Lambda in aLambda
 aList Match (
  List:[]
   List:[]
  ( List:Make: Head Tail )
   List:Make:
     ^@( Head aLambda do )
     ^@( Tail aLambda call.me )
 ) // aList Match
; // List:Map

Ну и вызов:

List:Make 1 List:Make 2 List:Make 3 List:Make 4 List:Make 5 List:[]
 // строим список [ 1 2 3 4 5 ]
IsOdd
 // отбираем нечётные
List:Map
( ToString + ' ' )
 // тут приводим к строке и добавляем пробел
List:Map
List:For:
 // перебирается список
Print
 // печатаем каждый элемент

Напечатано:
1 3 5

Ну и вызов:

List:Make 1 List:Make 2 List:Make 3 List:Make 4 List:Make 5 List:[]
 // строим список [ 1 2 3 4 5 ]
IsOdd
 // отбираем нечётные
List:Map
List:Count
Print
 // печатаем List:Count

Напечатано:
3

Пока выглядит вроде некузяво.

Но у меня же скрипты изначально императивные. И ОПЗ (обратная польская запись).

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

Если это значение, то выражение - вычислено полностью.

Если ссылка на функцию, то вычислено не полностью.

Тут и проявляется "ленивость".

Когда в узле вычисляется функция, то в узел записывается результат вычисления вместо ссылки на функцию.

Тут проявляется кеширование результатов. Ранее вычисленные функции - больше не вычисляются. А представляются значениями записанными в узлах графа/дерева.

Ну и код на Haskell:

xs = 1 : map (*2) xs

Выглядит так:

List def xs
 List:Make:
  1
  ( call.me ( 2 * ) List:Map )
; // List:Map

Я честно скажу - не проверял, но должно сработать.

Завтра проверю.

Бесконечной рекурсии там точно не будет.

При вызове xs.

Она сконструирует список:
 1 : reference to expression ( xs ( 2 * ) List:Map )

А вот применится ли  Map к правильному значению - вопрос пока открыт.

Возможно Map будет применяться не к xs, а к 1 ВСЕГДА.

Тут вопрос пока открыт. Надо проверить.

Возможно надо написать так:

List def xs
 List:Make:
  1
  ( Result ( 2 * ) List:Map )
; // List:Map

Есть и такой вариант. И он (как мне кажется) - ближе к истине.

Ибо call.me - это ссылка на функцю, а Result - это уже вычисленное значение.

Тонкая грань.

Или так:

List def xs
  List VAR myResult
 List:Make:
  1
  ( myResult ( 2 * ) List:Map )
 >>> myResult
 myResult
; // List:Map)

Хотя и это можно скрыть за "синтаксическим сахаром".

среда, 25 октября 2017 г.

ToDo. Сделать глобальную writeonly "переменную" _

Сделать глобальную writeonly "переменную" _

WriteOnly _

Туда можно писать, но нельзя читать.

Это аналог devNull.

Это нужно для pattern matching'а.

Update. Сделал.

ToDo. Сделать match вместо RULES

Сделать match:

Чтобы вместо:

VAR head
VAR tail

RULES
 ( aList Empty == )
  Empty
 ( aList .ConstructedWith: List )
  (
   aList .getValues ( @ head @ tail )
   head : tail
  )
 DEFAULT
  aList
 >>> Result
;

Или:


VAR head
VAR tail

RULES
 ( aList .ConstructedWith: Empty )
  Empty
 ( aList .ConstructedWith: List )
  (
   aList .getValues ( @ head @ tail )
   head : tail
  )
 DEFAULT
  aList
 >>> Result
;

Написать:

Match aList
 ( Empty :  Empty )
 ( matched List ( head tail ) : ( head : tail ) )
 ( Default : aList )
 >>> Result
;

Чтобы можно было pattern matching делать.

И переменные (head и tail в нашем случае) определялись в нужном контексте.

Или даже так:

Match aList
 ( Empty ?  Empty )
 ( matched List head tail ? ( head : tail ) )
 ( matched Join head tail ? ( head : tail ) )
 ( Default ? aList )
 >>> Result
;

Или даже так:

Match aList
 ( Empty ?  Empty )
 ( List head tail ? ( head : tail ) )
 ( Join head tail ? ( head : tail ) )
 ( Default ? aList )
 >>> Result
;

Haskell. Pattern matching

checkIP :: IPAddress -> String
checkIP (IPv4 address) = "IPv4 is '" ++ address ++ "'."
checkIP IPv4Localhost  = "IPv4, localhost."
checkIP (IPv6 address) = "IPv6 is '" ++ address ++ "'."
checkIP IPv6Localhost  = "IPv6, localhost."
Очень глубоко на самом деле.

Я давно о подобном думал. А решение оказывается "лежало на поверхности".

вторник, 24 октября 2017 г.

Data 1. Haskell vs мои скрипты

data List = List a List | []

Выглядит так:

Data List
 Constructor List (
  Anonim ANY
  Anonim List
 ) // List

 Constructor [] ()
; // List

Или с именами полей:

Data List
 Constructor List (
  Named ANY Head
  Named List Tail
 ) // List

 Constructor [] ()
; // List

Или в операторном виде:

Data List
 Constructor List (
  Left Anonim ANY // параметр слева
  Anonim List // параметр справа
 ) // List

 Constructor [] ()
; // List

Тогда конструктор List вызывается так:

1 List 2 List []

Строит:

[ 1 2 ]

О! Я понял как pattern matching сделать.

В общем я могу сделать head:tail вместо .Split Head Tail.

 aList .Split Head Tail

Можно сделать так (под капотом):


MATCH List:Make ( Head Tail )


Тут говорим, что надо применить pattern matching к конструктору List:Make и положить результаты в ( Head Tail )


А потом MATCH можно спрятать в и вычислять его в зависимости от того с какой стороны от знака равно (=) находится List:Make.


Учитывая, что "экземпляр объкта" знает про конструктор, которым он был создан, то выражение:


MATCH List:Make ( Head Tail )


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



MATCH ( Head Tail )



Тогда навскидку:

LIST FUNCTION f
  LIST IN aList
 ANY VAR Head
 LIST VAR Tail
 aList .Split Head Tail
 LIST:Make 1 Map ( 2 * ) Tail
 >>> Result
; // f

Можно переписать так:


LIST FUNCTION f

  LIST IN aList
 ANY VAR Head
 LIST VAR Tail
 MATCH aList  ( Head Tail )
 LIST:Make 1 Map ( 2 * ) Tail
 >>> Result
; // f


Или даже так:

LIST FUNCTION f
  LIST IN aList
 ANY VAR Head
 LIST VAR Tail
 MATCH aList
  []
   []
  ( Head Tail )
   ( LIST:Make 1 Map ( 2 * ) Tail )
 ; // MATCH
 >>> Result
; // f


Или так:


LIST FUNCTION f
  LIST IN aList
 MATCH aList
  []
   []
  ( ANY VAR Head
    LIST VAR Tail
    Head Tail )
   ( LIST:Make 1 Map ( 2 * ) Tail )
 ; // MATCH
 >>> Result
; // f

Data. Haskell vs мои скрипты

data MyEnum = One|Two

One == Two

Выглядит так:

Data MyEnum
 Constructor One ()
 Constructor Two ()
;

One Two == 

Списки. Haskell vs мои скрипты

f x:xs = 1 : map (*2) xs

Выглядит так:

LIST FUNCTION f
  LIST IN aList
 ANY VAR Head
 LIST VAR Tail
 aList .Split Head Tail
 LIST:Make 1 Map ( 2 * ) Tail
 >>> Result
; // f

У меня нет pattern-matching'а. Поэтому Split.

Ну и да - скрипты у меня конечно императивные. Но с ленивыми вычислениями. И без лишнего копирования списков.

Map ( 2 * ) Tail - не копирует Tail.

LIST:Make - тоже. Там Связный список.


Про замену .Split написано тут - http://programmingmindstream.blogspot.ru/2017/10/data-haskell-vs.html?m=1

 aList .Split Head Tail

Можно сделать так (под капотом):

MATCH List:Make ( Head Tail )

Тут говорим, что надо применить pattern matching к конструктору List:Make и положить результаты в ( Head Tail )


А потом MATCH можно спрятать в и вычислять его в зависимости от того с какой стороны от знака равно (=) находится List:Make.

понедельник, 23 октября 2017 г.

Haskell. Цитата

"Применение конструктора типа к существующему типу порождает некий новый тип, и это очень мощная техника, используемая в Haskell почти на каждом шагу. Например, если нам нужно завернуть в опциональное значение уже не String, а ранее упомянутый Text, мы ничего не должны менять в конструкторе Maybe"

Вопрос. Haskell

А есть ли "скриптовые" реализации Haskell?

Ну чтобы можно было использовать Haskell в качестве скриптового языка внутри проекта написанного на Delphi.

Ну примерно так, как обычно используют Python.

воскресенье, 22 октября 2017 г.

Ссылка. Haskell Platform

https://www.haskell.org/platform/windows.html

Ссылка. Haskell

http://eax.me/haskell-first-app/

Вопрос. Haskell

А кстати можно так написать?
h :: Int -> Int
h x + 1 = x

h 3 == 2

Правило вывода сработает?

Ну это я таким извращённым способом операцию декремента реализую. Теоретически.

Понятно, что можно написать напрямую:
h x = x - 1

но мне интересна внутренняя природа устройства правил вывода.

UPDATE. На вопрос я вроде бы нашёл ответ - в pattern matching'е участвуют не функции, а конструкторы. Поправьте, если я не прав.

Ссылка. Take

четверг, 19 октября 2017 г.

first:second:tail

first:second:tail

ARRAY VAR A
VAR first
VAR second
VAR tail

A .Split ( first second tail )

first:second:tail

ARRAY VAR A
VAR first
VAR tail

A .Split ( first tail )

first:second:third:tail

ARRAY VAR A
VAR first
VAR second
VAR third
VAR tail

A .Split ( first second third tail )

ToDo. Изменение положения оператора

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

1 2 + =>
1 :`  + 2

Или:

1 2 + =>
1 `  + 2

operator `
 in ValueLeft
 ^ in Lambda
 ^ in ValueRight
 ValueLeft // - значение слева
 ValueRight DO // - разыменовываем значение справа
Lambda DO // - вызываем "лямбду"/"функтор"
;

Реализация sum на Haskell

sum ( [] ) = 0
sum (x:xs) = x + sum (xs)

Concat на Haskell

Как нить так (это сходу и наверняка не совсем верно):
concat' [] = []
concat' ([]:ys) = concat' ys
concat' ((x:xs):ys) = x : concat' (xs:ys)

Ссылка. Чат про Haskell для новичков

https://gitter.im/ruHaskell/novice

среда, 18 октября 2017 г.

Ссылка. Чат про Haskell

https://gitter.im/ruHaskell/forall

Ссылка. О Haskell по-человечески

https://www.ohaskell.guide/init.html

Отличная книга кстати. Жаль короткая.

Вопрос

[ [ 1 2 ] [ 3 4 ] ] как на Haskell преобразовать к [ 1 2 3 4 ] ?

Ну или на каком-нибудь другом функциональном языке?

У меня так:

[ [ 1 2 ] [ 3 4 ] ] .Fold

Но у меня же - "на коленке". Хочется понимать как "у людей" устроено.

А [ 1 2 ] .Join [ 3 4 ] как?

А [ 1 2 ] .Map .ToString как?

А [ 1 2 ] .Filter .IsOdd как?

А 0 [ 1 2 ] .For + /*сумма элементов*/ как?

Надеюсь, что смысл примеров понятен. Хотя они конечно "на коленке".

Если они всё же понятны, то может быть кто-то откликнется и расскажет как подобное на настоящих языках можно сделать?

ToDo. Сериализация

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

Надо эту тему "покурить".

ToDo

Опубликовать конструкторы от tryFinally и tryExcept.

И ещё ко всем конструкторам сделать "пару" - CreateWithClass/XXXCreateWithClass.

Чтобы через эти конструкторы можно было создавать экземпляры по известной ссылке на класс.

Для финальных классов не делать такую пару.

пятница, 13 октября 2017 г.

Массивы

Range: 0 10 // диапазон от 0 до 10
.Filter .IsOdd // отбираем нечётные
.Join ( // объединение
Range: 20 30 // диапазон от 20 до 30
.Filter . IsEven // отбираем чётные
)
.Join // объединение
 [ 31 32 33 ] // массив из элементов 31 32 33
.Join // объединение
ProduceArray: 100 Random // генерируем массив из 100 случайных элементов
.Join // объединение
ProduceArray: 200 ReadIntFromFile: File // читаем 200 элементов из файла
.Join // объединение
[ 100 1024 ] // массив из элементов 100 1024
.Map ( + 1 ) // прибавлям 1
.Map ( * 3 ) // умножаем на 3
.Map .ToString // преобразуем к строке
.Slice 2 // разбиваем массив на пары
.Slice 3 // разбиваем массив на тройки
.Trunc 10 // обрезаем массив до 10 элементов

Array Var A // объявляем переменную типа массив

>>> A // кладём результат вычислений в A

A .Copy // делаем копию массива

>>> A // кладём результат вычислений в A

A .Join A // объединяем A с A

>>> A // кладём результат вычислений в A

A .Fold // преобразуем массив массивов обратно к плоскому списку
.Map .ToInt // преобразуем строки обратно к числам
.RemoveDuplicates // удаляем дубликаты
.Sort // сортируем массив
.Invert // инвертируем массив
.Slice 2 // разбиваем на пары
.Map ( 0 .Swap .For + ) // суммируем пары
.Sort // сортируем
.For // перебираем массив
.Print // печатаем элемент массива

Все вычисления - "ленивые".

Кроме Copy, RemoveDuplicates и Sort.

понедельник, 9 октября 2017 г.

Тут подумалось

Прям самопроцитирую: "Я к тому, что "аллегория" это скажем так "некоторое преобразование" одного более сложного объекта к другому, более простому. Как хеш-функция.

Можно сравнивать строки, а можно их хеш-функции. Хеш-функции сравнивать проще. Но их сравнение менее "однозначно". Ибо там есть "потеря полноты картины". Но зачастую полнота картины и не нужна. Собственно это тогда и есть аллегория.
"

Рекомендую книгу

https://www.ozon.ru/context/detail/id/35009452/
https://m.livelib.ru/work/1002522273/reviews-abstraktnye-tipy-dannyh-v-yazyke-atpaskal-anatolij-dedkov

четверг, 5 октября 2017 г.

Научился делать "настоящие" immutable объекты

Научился делать "настоящие" immutable объекты.

Через перекрытие NewInstance + VirtualAlloc + VirtualProtect.

Перекрываем NewInstance - там аллоцируем память через VirtualAlloc (там есть тонкости с размером страниц и стратегией выделения памяти).

Потом заполняем поля объекта в конструкторе. И "закрываем" доступ на запись через VirtualProtect + Page_ReadOnly - в AfterCreate.

Потом в FreeInstance (или destroy - тут есть тонкости) опять разрешаем доступ.

Получаем гарантировано неизменяемые объекты.

Например для паттерна flyweight.

Или например для случая:

ConstString = interface
 Function S: PChar;
End;

Ничто не мешает сделать так:

Var Cs: ConstString;

Cs := Factory.GetCs('value');
Cs.S^ := 'xxx';

Т.е "кишки" объекта будут испорчены.

А вот virtualProtect + page_readonly - решают эту проблему.

Просто

Крысу поймал, телевизор "починил". Теперь бы на работе с заковыристой ошибкой разделаться бы.

Компонент разрушается, но из списка компонент родителя - не удаляется.

А под отладчиком - вроде всё хорошо.

Только под Delphi Tokyo. Под Berlin и Delphi 7 - всё хорошо.

Уже разные способы ловли испробовал. Вплоть до VirtualProtect...

Меняю способ распределения памяти под компонент - ошибка уходит.

Не помогает...

Завтра тупо начну логировать убитые компоненты с их адресами и адресами родителей. И trhreadId.

Смущает тот факт, что ошибка под Tokyo есть, а под Berlin - её нет. Обязательно напишу, если чего накопаю. Хотя конечно скорее всего это какая-то наша ошибка.

среда, 4 октября 2017 г.

Иногда случаются проезды по памяти при использовании getMem/freeMem

Иногда случаются проезды по памяти при использовании getMem/freeMem.

Как их искать?

VirtualAlloc/VirtualProtect далеко не всегда помогают. В силу их специфики.

Как быть?

Придумал вот что:

Const
 Filler : array [0..1] of integer = (high(integer), high(integer) -1);

SafeGetMem: Pointer
Var allocated: pointer;
 GetMem(result, SizeOf(pointer) + sizeOf(Filler));
 GetMem(allocated, aSize + SizeOf(pointer));

Move(result^, result^, sizeOf(result));
Move(result + SizeOf(result)^, Filler, SizeOf(filler));

Move(allocated^, result^, SizeOf(result));

Result := allocated + SizeOf(result);

А в safeFreeMem - проверяем все инварианты.

ToDo

Сделать newInstance через getMem + offset + high(pointer).

Возможно так можно будет отследить - кто ездит по памяти.