Войти в систему

Home
    - Создать дневник
    - Написать в дневник
       - Подробный режим

LJ.Rossia.org
    - Новости сайта
    - Общие настройки
    - Sitemap
    - Оплата
    - ljr-fif

Редактировать...
    - Настройки
    - Список друзей
    - Дневник
    - Картинки
    - Пароль
    - Вид дневника

Сообщества

Настроить S2

Помощь
    - Забыли пароль?
    - FAQ
    - Тех. поддержка



Пишет kouzdra ([info]kouzdra) в [info]programming
@ 2005-11-02 00:06:00


Previous Entry  Add to memories!  Tell a Friend!  Next Entry
Для затравки
Представление данных в O'Caml (и вообзще -ML):

На самом деле - оно очень красиво. Что самое забавное - на С изобразить
это можно, но задолбаешся. Итак:

1) никакого RTTI в O'Caml нет. Нет настолько, что сделать downcasting в
объектном расширении просто невозможно технически.

2) Представление данных там стольже прямолинейно и тупо как в Pascal.

3) Что есть - есть следующее:

Все типызначения представляются в O'Caml одним словом и делятся на boxed
и unboxed. Boxed - это те, которые лежат в куче, а представляются адресом в оной.
Unboxed - представляются своим физическим значением, сдвинутым влево на 1 бит и
1 в младшем бите. Потому, собственно, int в O'Caml не 31, а 30 битов.

Unboxed values - int, char, bool, unit (да - у него есть значение - нолик),
и элементы union's (АТД) без "of ...".

Boxed - все остальное. Соотвественно как представляются

type 'a Tree = E | N of 'a tree * 'a * 'a tree

Вариант E - просто 0 (с учетом сдвига и битика - 1)
вариант N - адрес блока, в котором лежит три слова - элементы узла.
Сам тэг (1) лежат в управляющем слове по смещению -4 от адреса.

Вот. Общая идея, надеюсь, понятна. На самом деле вся "информация о типах" в run-time
сводится к этому самому младжему битику, который отличает значение от адреса. Более
- ничего. Какие-то вещи (вроде сериализации или полиморфного сравнения) на этом
сделать можно. Но вообще-то мало что можно.


(Добавить комментарий)


[info]qwerty
2005-11-02 02:20 (ссылка)
Представление в точности как в Смолток-80 ВМ.

Удивительно, что почему-то на парах (данные,интерфейс) виртуальные машины редко делают.

(Ответить) (Ветвь дискуссии)


[info]kouzdra
2005-11-02 09:33 (ссылка)
Есть забавный момент - сделать приличную сборку мусора в ML без этого битика
никому не удалось. Проблема - именно в информации о типах. Когда у значения
в принципе нет единственно верного типа - возникают большие проблемы с табличками
(в теории они разрешимы, но лезут экспоненты)

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

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]qwerty
2005-11-02 10:48 (ссылка)
Это-то понятно. Я немного про другое. Битик - это в сущности простейший тег для различения непосредственно представленного значения от указателя на структуру в памяти. Значение же вместе с тегом всегда представлено одним словом. Довольно естественно это представление расширить до двухсловного - пары (данные, интерфейс). Кайф такого представления в большом пространстве значений тега. Поэтому не обязательно его кодировку повсюду зашивать, можно играть на расширении, при условии совместимости типов можно подменять интерфейс и т.д. Недостаток же в том, что пара, вообще говоря, занимает больше места. С этим можно до определенной степени бороться компиляторным умом.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2005-11-02 11:00 (ссылка)
Что-то похожее есть в Haskell. Но там хитрее (и, по моему, лучше):
Там "классы типов", то есть некий интерфейс, которым обладает тип.
Ну классика:
   class Eq a where (==): a -> a -> Bool

И, скажем, реализация;
   instance Eq a => Eq [a] where a == b = ...

То есть тут instance задает правило для реализации сравнения
списков, если есть сравнение элементов.

Кайф в возможности писать вещи вроде:
count: Eq a => a - > [a] -> Int
count e [] = 0
count e (a:b) | a == e    = 1 + count e b
count e (a:b) | otherwise = count e b


Реализация простая - "предикат" (Eq a => ...) в типе значения (функции)
означает, что у функции есть неявный параметр - табличка с "интерфейсом".
А всякие instance задают правила, позволюще ее получить.

С одной стороны - тут интерфес полностью отвязан от типа (это что-то вроде "точки
зрения на тип"), с другой - все хорошо. Наследованию соотвестует описание
класса, у которого "пререквизитом" является набор других классов.

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

Система мне очень нравится, правда в Haskell как всегда навернули ее
сверх меры.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]potan
2005-11-02 12:00 (ссылка)
Я считал что в OCaml классы так же сделаны. Иначе как реализовать
# let f = fun c -> c#xex;;
val f : < xex : 'a; .. > -> 'a = <fun>

(Ответить) (Уровень выше)


[info]qwerty
2005-11-02 12:17 (ссылка)
Ага. В своем объектном Форте я тоже так делал, но не смог раскрутиться. Сейчас, наверно, смог бы за счет ума компилятора.

Кажется, я бы сделал даже иначе - каждое значение состоит как минимум из "головы", из которой могут расти "хвосты" (Г.Цейтин уже над этим 15 дет назад отсмеялся). Тип головы должен быть в точности выводиться во время компиляции. Каждый хвост снабжается константным тегом представления. В голове логически обязан быть представленный ссылкой интерфейс. Физически умный компилятор может интерфейс извести - например, если известно, что для данного типа его подменять запрещено. Динамика типов делается через подмену интерфейсов, каковые должны быть совместимы с типом. Если очень хочется, можно завести подтипы на головах, но подтипы очень ограниченные - тип в точности известен, присваивать надтипу можно (при условии совместимости ьекущего интерфейса с надтипом), но при этом надтип и получается путем отбрасывания полей.

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

(Ответить) (Уровень выше)


[info]potan
2005-11-02 11:55 (ссылка)
А почему сборщик мусора не может знать типы величин, с которыми он работает?
Когда он обрабатывает переменную в стеке - тип ему может быть предоставлен компилятором. Дальше он бегает по ссылкам, каждый раз зная ее тип. Что еще ему надо?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2005-11-02 11:57 (ссылка)
Полиморфизм мешает - взять тот же 'a tree - хрен поймешь, что там на самом деле лежит.
Иногда так и просто мономорфного типа просто нет - [ []; [] ] - 'a list list и больше
про него ничего сказать нельзя.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]potan
2005-11-02 12:01 (ссылка)
Может быть. Надо подумать.

(Ответить) (Уровень выше)


[info]qwerty
2005-11-02 12:31 (ссылка)
В принципе, наверно, можно тег вынести из значения в мапку в объемлющей структуры, а про локалы узнавать от компилятора, но это примерно тот же хер, только сбоку - полноразрядные целые, но более тяжелые присваивания (хотя как раз их быть почти не должно).

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2005-11-02 12:58 (ссылка)
Так понимаешь - вот есть у тебя параметр типа 'a tree. Больше тебе
компилятор ничего сказать не может, потому как сам не знает, что
там будет (и в зависимости от - там разное).

Или еще хуже:
let l1 = [ []; [] ]
let l2 = [1]::l1
let l3 = ['a']::l1


l1: 'a list list
l2: int list list
l3: char list list

И при этом tl l2 == tl l3 == l1
то есть - l1 - часть обоих списков

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]qwerty
2005-11-02 13:06 (ссылка)
Да понимаю я. Типы (в минималистском смысле - непосредственное значение или указатель) локалов он знает. От вынесения тегов из полей в отдельную мапку той же лежащей в памяти структуры их количество не меняется. Мапка инициализируетя в момт создания структуры и должна правиться только при присваиваниях полям. Хер практически тот же, только целые полноразрядные, а присваивания более тяжелые, и компидятор в точках сборки мусора должен генерить мапки для локалов.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2005-11-02 13:13 (ссылка)
Типы (в минималистском смысле - непосредственное значение или указатель) локалов он знает

Фиг тебе. Вот например -
let f l = 
   match l with
    a::b -> ...

Про локал a он тут вообще ничего не знает.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]qwerty
2005-11-02 13:18 (ссылка)
Значит, придется ему иногда и мапку локалов править. Не очень приятно, но не фатально. Не все локалы таким мерзким свойством обладают, их можно и пожать. От полноразрядной арифметики много кайфов.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2005-11-02 13:25 (ссылка)
На самом деле об это долго долбились, додолбились до того, что
типы реконструировать в динамике почти всегда можно (а в тех
случаях, когда нельзя - это значит, что объект можно сносить на
хрен). Но получается сложно и в worst case - с экспонентами.

В принципе, если бороться за 32 бита - то надо скорее что-то
вроде cyclone учудять.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]qwerty
2005-11-02 20:13 (ссылка)
Это я скорее в порядке упражнения. С битиком в мл. разряде явно проще.

(Ответить) (Уровень выше)


[info]polytheme
2005-11-02 10:11 (ссылка)
ага. а есть ли, интересно, наследование ? большой процент наследования в библиотеках C++-подобных языков - это template method, и в функциональных языках вместо виртуальных функций есть closures. далее идут интрузивные контейнеры, вместо которых, видимо, параметризованные функции и типы. но вот, допустим, нам надо где-то создать объект и им попользоваться, объект, удовлетворяющий данному контракту. в других языках на вход метода подается абстрактная фабрика, производящая объекты. а в OCaml это возможно ?

и еще вопрос - сворачивает ли OCaml хвостовую рекурсию (вроде бы должен, как ему иначе добиться высокой производительности ? за счет возможности использования императивного стиля если, то это неинтересно)

(Ответить) (Ветвь дискуссии)


[info]kouzdra
2005-11-02 10:32 (ссылка)
Там есть довольно интересная и мощная объектность, я может потом разрожусь
описанием.

Но "канонический" ML-ный подход к параметризации кода - параметризованные
модули (ака функторы). Примеры - например в стандартной либе Map и Set. Ну
и просто полиморфные функции и замыкания. На них можно много чего изобразить,
на самом-то деле: например функция-счетчик (выдает последовательные значения):

let counter =
let n = ref 0 in
fun () -> incr n; !n

Оно конечно императивно, но счетчик полностью инкапсулирован.

PS: Хвостовую рекурсию сворачивают все функциональные языки, это просто
требование - иначе нельзя быть уверенным что не влетишь в переполнение стека.

(Ответить) (Уровень выше)


[info]dluciv [livejournal]
2005-11-02 11:46 (ссылка)
Ну вообще-то потуги сделать какой-никакой RTTI - были, например, Coca-ML.
Но учитывая то, что объекты в Ocaml, как бы это сказать, сбоку (в том числе, от основной системы типов), RTTI, наверное, и не нужно. Все-таки это функциональный язык с кой-какой поддержкой объектов, а не объектно-функциональный.

(Ответить)


[info]potan
2005-11-02 13:39 (ссылка)
А OCaml не портировали на машины с аппаратной поддержной тегов? Вроде AS/400 (или даже E2K ;-))?

(Ответить)


[info]potan
2005-11-09 13:54 (ссылка)
Кстати, можно же разработать компилятор с C (или Cyclone), с тем же представлением данных.
Это бы упростило интеграцию GC с самыми разными языками. Да и языков, использующих GC между собой.

(Ответить)