Войти
ФлеймФорумПрограммирование

Революционные идеи в сфере языков программирования (6 стр)

Страницы: 15 6 7 837 Следующая »
#75
10:59, 7 янв. 2020

Delfigamer
> Интерфейс может сам же задать дефолтную реализацию своего действия:
>
И что в этом хорошего? Интрефейс-описание контракта. Только это он и должен делать


#76
(Правка: 18:49) 16:20, 24 фев. 2020

Концепт-арт.

-- Std/Box.st

language SampleText

uses Unsafe

export
class Box[ T ]
    const Ptr = Unsafe.Pointer[ T ]

    local buf: Ptr

    -- Box[ T ][]
    default
    function Create[]
        : current class
    do
        return current class[
            buf -> Ptr.Null[] ]
    end

    -- Box[ T ][ elem ]
    default
    function Create[
        local elem: T ]
        : current class
    do
        local self = current class[
            buf -> Ptr.Alloc[ 1 ] ]
        self.buf.Attach[ consume elem ]
        return self
    end

    -- release box
    -- also called implicitly when a local goes out of scope
    default
    function Destroy[
        consume self: current class ]
    do
        if self.buf then
            release self.buf.Detach[]
            self.buf.Free[]
        end
    end

    -- box[]
    default
    function Deref[
        self: current class ]
        : OptionalExternal[ T ] of self
    do
        if self.buf then
            return MakeOptionalExternal[ self.buf.Deref[] ]
        else
            return NullOptional[]
        end
    end

    function DerefModify[
        modify self: current class ]
        : OptionalModify[ T ] of self
    do
        if self.buf then
            return MakeOptionalModify[ modify self.buf.Deref[] ]
        else
            return NullOptional[]
        end
    end

    -- box[ value ]
    -- box[] = value
    default
    function Attach[
        modify self: current class,
        default local value: T ]
    do
        if self.buf then
            release self.buf.Detach[]
        else
            self.buf = Ptr.Alloc[ 1 ]
        end
        self.buf.Attach[ consume value ]
    end

    function Detach[
        modify self: current class ]
        : Optional[ T ]
    do
        if self.buf then
            local value = self.buf.Detach[]
            self.buf.Free[]
            self.buf = Ptr.Null[]
            return MakeOptional[ value ]
        else
            return NullOptional[]
        end
    end
end

-- if box then ...
parametric [ T ]
function System.Test[
    self: Box[ T ] ]
    : Boolean
do
    return Test[ self.buf ]
end

-- with value from box do...
parametric [ T ]
function System.Enter[
    self: Box[ T ],
    modify body: closure type [ : T ] ]
do
    local opt = self[]
    with value from opt do
        body[ value ]
    end
end

-- with modify value from box do...
parametric [ T ]
function System.Enter[
    self: modify Box[ T ],
    modify body: closure type [ modify : T ] ]
do
    local opt = self.DerefModify[]
    with modify value from opt do
        body[ modify value ]
    end
end

-- with consume value from box do...
parametric [ T ]
function System.Enter[
    self: modify Box[ T ],
    body: closure type [ consume : T ] ]
do
    local opt = self.Detach[]
    with consume value from opt do
        body[ consume value ]
    end
end

#77
17:33, 24 фев. 2020

Delfigamer
> Признайтесь, у каждого из нас были идеи нового языка.

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

Понятий «tooling» и «iteration time» я тогда не знал, но в полной мере на себе ощутил, что язык сам по себе, в отрыве от средств разработки всевозможного калибра, нафиг не сдался, каким бы хорошим он ни был.

Позывов придумать новый язык больше не было.

#78
(Правка: 18:37) 17:54, 24 фев. 2020

Delfigamer
> Признайтесь, у каждого из нас были идеи нового языка.

Честно говоря НЕ было...

И кстати ИМХО шибкой Революционности  в вашей идеи по-моему тоже нету...

РЕВОЛЮ́ЦИЯ  - Коренной переворот в какой-н. области жизни, науки, производства.

PS А вот если бы Вы - к примеру - Запилили бы  Компилятор  с самообучаемым AI - такой чтобы... ухитрялся адекватно сбилдить даже программку произнесенную на классическом китайском языке и подтвержденный дулом мушкета (C)  - это было бы хотя б - по крайней мере - Прикольно... ;)

#79
18:12, 24 фев. 2020

Стохастическое программирование когда какой то код выполняется с определенной вероятностью.
Применение: моделирование физических и химических процессов.

#80
(Правка: 18:38) 18:24, 24 фев. 2020

gamedevfor
> Стохастическое программирование когда какой то код выполняется с определенной
> вероятностью.
> Применение: моделирование физических и химических процессов.

Не... Это не то че я имел в виду...  Просто наверное Вы цитату НЕ узнали... ;)

Я имел в виду именно Компилятор с самообучающимся AI ... который бы не просто выполнял бы любой бредо код...  А на основе того "невнятного мычания" который бы  ему выдавал "Программист" сам бы восстанавливал  на основе данного "мычания" поставленную ПЕРЕД Кодером задачу в ее полном объеме - и генерил бы адекватную ей Программу... 

В принципе... Ничего ИМХО в этом ПРИНЦИПИАЛЬНО сложного нет... Ибо все задачки с которыми работают кодеры  достаточно стандартные... И вообще у людей все запросы довольно таки примитивные... И весьма шаблонные...  Как говорится куда все... Туда и я... ;) 

 

#81
(Правка: 18:49) 18:43, 24 фев. 2020

Концепт-арт.

-- Core/Optional.st

language SampleText

export
interface OptionalExternal[ T ]
    requires
    function System.Test[
        self: current class ]
        : Boolean

    requires
    function System.Enter[
        self: current class,
        modify body: closure type [ : T ] ]
end

export
interface OptionalModify[ T ]
    requires
    interface OptionalExternal[ T ]

    requires
    function System.Enter[
        modify self: current class,
        modify body: closure type [ modify : T ] ]
end

export
interface Optional[ T ]
    requires
    interface OptionalModify[ T ]

    requires
    function System.Enter[
        consume self: current class,
        modify body: closure type [ consume : T ] ]
end

class NullOptionalType
end

parametric [ T ]
implementation for NullOptionalType of Optional[ T ]
    function System.Test[
        self: current class ]
        : Boolean
    do
        return false
    end

    function System.Enter[
        self: current class,
        modify body: closure type [ : T ] ]
    do
    end

    function System.Enter[
        modify self: current class,
        modify body: closure type [ modify : T ] ]
    do
    end

    function System.Enter[
        consume self: current class,
        modify body: closure type [ consume : T ] ]
    do
    end
end

class OptionalExternalObject[ T ]
    external data: T
end

parametric [ T ]
implementation for OptionalExternalObject[ T ] of OptionalExternal[ T ]
    function System.Test[
        self: current class ]
        : Boolean
    do
        return true
    end

    function System.Enter[
        self: current class,
        modify body: closure type [ : T ] ]
    do
        body[ self.data ]
    end
end

class OptionalModifyObject[ T ]
    modify data: T
end

parametric [ T ]
implementation for OptionalModifyObject[ T ] of OptionalModify[ T ]
    function System.Test[
        self: current class ]
        : Boolean
    do
        return true
    end

    function System.Enter[
        self: current class,
        modify body: closure type [ : T ] ]
    do
        body[ self.data ]
    end

    function System.Enter[
        modify self: current class,
        modify body: closure type [ modify : T ] ]
    do
        body[ modify self.data ]
    end
end

class OptionalDataObject[ T ]
    local data: T
end

parametric [ T ]
implementation for OptionalDataObject[ T ] of Optional[ T ]
    function System.Test[
        self: current class ]
        : Boolean
    do
        return true
    end

    function System.Enter[
        self: current class,
        modify body: closure type [ : T ] ]
    do
        body[ self.data ]
    end

    function System.Enter[
        modify self: current class,
        modify body: closure type [ modify : T ] ]
    do
        body[ modify self.data ]
    end

    function System.Enter[
        consume self: current class,
        modify body: closure type [ consume : T ] ]
    do
        self.Destroy[
            produce data -> local tmp ]
        body[ consume tmp ]
    end
end

export
function NullOptional[]
    : NullOptionalType
do
    return NullOptionalType[]
end

export
parametric [ T ]
function MakeOptional[
    local value: T ]
    : Optional[ T ]
do
    return OptionalDataObject[
        consume data -> value ]
end

export
parametric [ T ]
function MakeOptionalModify[
    modify value: T of owner ]
    : OptionalModify[ T ] of owner
do
    return OptionalModifyObject[
        modify data -> value ]
end

export
parametric [ T ]
function MakeOptionalExternal[
    external value: T of owner ]
    : OptionalExternal[ T ] of owner
do
    return OptionalExternalObject[
        data -> value ]
end

#82
(Правка: 19:56) 19:47, 24 фев. 2020

Optional работает в паре со специальной синтаксической конструкцией:

with <arguments> from <source> do
    <suc-branch>
else
    <fail-branch>
end

По смыслу, with пытается извлечь данные из контейнера, в случае успеха, эти данные присваиваются локальным переменным и обрабатываются в первой ветке, в случае неудачи, выполняется вторая ветка.
При таком раскладе, время жизни внутренних переменных совпадает со временем жизни данных - в частности, при отсутствии данных эти переменные никогда и не попадают в область видимости.
Под капотом, конструкция работает так:
local suc_closure_1 =
    closure [ <arguments> ]
    do
        <suc-branch>
    end
local suc_flag = false
local suc_closure_2 =
    closure [ <arguments> ]
    do
        suc_flag = true
        suc_closure_1[ <arguments> ]
    end
System.Enter[ <source>, modify suc_closure_2 ]
if not suc_flag then
    <fail-branch>
end

По сути - компилятор заворачивает тело конструкции в анонимную функцию, и затем передаёт эту функцию вместе с тестируемым объектом в систему динамического диспатча - вызов System.Enter.
Описанный выше Optional встраивается в систему таким образом:
- Публичный тип Optional[ T ] объявлен как interface. Это значит, что переменные этого типа могут хранить объекты разных типов - как OptionalDataObject[ T ], так и NullOptionalType.
- В момент дереференса контейнера - через конструкцию with - происходит динамический диспатч по текущему типу объекта в переменной. Если там NullOptionalType - то вызовется реализация, которая проигнорирует запакованное тело конструкции, что будет воспринято как неудача. Если там OptionalDataObject[ T ] - то вызовется его реализация, которая достанет данные из контейнера и передаст их в тело конструкции.

Будучи списком аргументов, <arguments> может содержать и режим доступа.
- Все три варианта поддерживают external (который выбирается по умолчанию). В этом режиме, на содержимое берётся читающая ссылка.
- OptionalModify и Optional поддерживают modify. В этом режиме берётся изменяющая ссылка. Если распаковывается владеющий Optional - то изменения будут ограничены только этим контейнером. Если распаковывается ссылочный OptionalModify - то через него изменения будут переданы тому объекту, от которого контейнер был изначально получен.
- Наконец, Optional можно распаковать с разрушением. В этом режиме контейнер уничтожается, и владение содержимым передаётся телу with-конструкции, которое, в свою очередь, может передать объект куда-то ещё, или же уничтожить его самостоятельно перед окончанием.

Создание структур производится через синтаксис вызова с именованными параметрами, где имена параметров соответствуют именам полей структуры.
Уничтожение может произойти автоматически - когда владеющая переменная теряется из области видимости; конструкцией release; а так же явным вызовом деструктора Destroy. В последнем случае, через принимающие аргументы можно "спасти" дочерние объекты структуры, забрав их владение. Разрушающая распаковка Optional как раз использует именно это.

Статический контроль за ссылками работает по принципу блокировок - для каждого объекта-ссылки сохраняется объект-родитель, к которому эта ссылка направлена; таким образом существование одного объекта накладывает блокировку на другой.
Блокировки распространяются наружу по иерархии объектов:

function RefToX[ modify p: FPoint ] : modify Float of p
do
    modify xref = p.x -- type of `xref` is `Float of p.x`
    return xref
end

local p = FPoint[ 1, 2 ] -- type of `p` is `FPoint`
do
    modify deepref = p.x -- type of `deepref` is `modify Float of p.x`
    local deepopt = MakeOptionalModify[ modify deepref ] -- type of `deepopt` is `OptionalModify[ Float ] of p.x`
    modify refpt = p -- error: p is locked by deepref
    modify refy = p.y -- ok: p.y is known to be free
end
modify shref = RefToX[ modify p ] -- type of `shref` is `modify Float of p`
modify refy = p.y -- error: p is locked in its entirety by shref
local shopt = MakeOptionalModify[ modify shref ] -- type of `shopt` is `OptionalModify[ Float ] of p`


Если цель блокировки теряется из видимости раньше, чем блокирующий объект или ссылка - компилятор выдаёт ошибку.
Поэтому, ссылочные конструкторы MakeOptionalModify и MakeOptionalExternal содержат дополнительные аннотации в прототипе, которые сообщают вызывающей стороне, куда ссылаются результаты функции и чего они блокируют.
Вот такая запись породила бы ошибку:
export
parametric [ T ]
function MakeOptionalModify[
    modify value: T ]
    : OptionalModify[ T ]
do
    return OptionalModifyObject[
        modify data -> value ]
end

Здесь временный объект - свежесозданный OptionalModifyObject - блокирует аргумент value. Ошибка возникает при попытке вернуть этот объект из функции - value, будучи аргументом, заканчивает свою жизнь вместе с функцией, а зависящий от него аниномный объект при этом передаётся вызывателю и продолжает жить.
Чтобы эта функция заработала, нужно добавить явные маркеры зависимостей:
export
parametric [ T ]
function MakeOptionalModify[
    modify value: T of owner ]
    : OptionalModify[ T ] of owner
do
    return OptionalModifyObject[
        modify data -> value ]
end

Теперь компилятор может проследить, что новый анонимный объект зависит только от owner напрямую, способен пережить потерю переменной value и действительно соответствует времени жизни, заявленному в прототипе.

#83
19:50, 24 фев. 2020

t800
> PS А вот если бы Вы - к примеру - Запилили бы Компилятор с самообучаемым AI -
> такой чтобы... ухитрялся адекватно сбилдить даже программку произнесенную на
> классическом китайском языке и подтвержденный дулом мушкета (C) - это было бы
> хотя б - по крайней мере - Прикольно... ;)
Изображение

#84
20:36, 24 фев. 2020

Delfigamer

+ Такой сгодится ;)
#85
(Правка: 21:15) 21:11, 24 фев. 2020

Delfigamer
> Признайтесь, у каждого из нас были идеи нового языка.
Язык, философия которого самое удобное чтение кода.
Мощная система автодокументации.
Возможность создать несколько представлений. Например:
- краткая, код больше напоминает математические формулы
- детальная, где вместо x,y,z всякие pathLength, entityLifetime и т.д.
- документация

#86
(Правка: 22:04) 21:56, 24 фев. 2020

Синтаксис — это не серьезно. Есть более интересные вещи для придумывания.

Например.

Представим ситуацию: вы — программист CS и перед вами задача отрисовать таблицу команд. Ну, знаете, ту, которая по Tab-у показывает у кого сколько фрагов, сколько убийств и так далее. Задача вроде-бы простая: все персонажи уже есть в сцене, нужно просто создать список, отсортировать его, и пробежаться for-ом.

Но есть проблема. Вам придется добавить к классу игрока какой-нибудь геттер, который вернет вам число фрагов. Но это число — не просто число, его еще откуда-то нужно взять. Простейший пример — вы где-то в физическом движке пишете, что класс персонажа должен запомнить сколько раз он в кого-то попал. Такой подход нарушает принцип единственной ответственности и делает код довольно грязным и запутанным (представьте, что у вас сотни подобных мест по всему коду).

Очевидно, что нужен какой-то промежуточный класс (назовем ему LeaderboardRow), который содержит именно информацию о фрагах, и, в том числе, и ссылку на персонажа.

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

Это плохо еще и потому, что этот конкретный LeaderboardRow знает, как устроены внутренности класса персонажа. Он знает, как читать никнейм и зависит от конкретной реализации класса персонажа. Мы могли бы решить проблему, инвертировав зависимость — сделать абстрактный интерфейс, который, с одной стороны, будет реализовывать класс персонажа, а с другой, на него будет полагаться LeaderboardRow для получения имени. Например, такой интерфейс мог бы называться IHaveUsername. Серьезно? Никто так не делает. Никто не делает интерфейсов, которые нужны ровно в одном месте для одной конкретной сущности. Это красивая идея в теории, но совершенно не возможная на практике.

Все, вилы.

И так и так — плохо.

Смысл в том, что и персонаж, который бегает по уровню и стреляет и строчка в таблице команд — суть один и тот же объект. Это две разных проекции одного и того же объекта. И, конечно, идея того, что один класс должен реализовывать методы для всех возможных способов его использования — даже умозрительно кажется плохой.

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

Это сложно тестировать, сложно поддерживать, сложно редактировать. Никому такой подход не понравится.

В языках с поддержкой ООП нужна еще одна сущность, помимо классов: проекции.

1. Проекция мимикрирует под объект. Ее можно передать по ссылке, сослаться, присвоить. У нее есть методы и свойства.
2. Один класс может иметь сколько угодно проекций.
3. Проекция не может иметь своего собственного состояния.
4. Проекция имеет доступ ко всем данным и методам базового класса.
5. Проекция может расширять или сужать интерфейс класса.
6. Проекция не имеет своего жизненного цикла: ее нельзя создать и уничтожить. Проекция становится невалидной, только когда уничтожается базовый для нее объект.

#87
(Правка: 2:24) 2:23, 25 фев. 2020

ychebotaev
> Но есть проблема. Вам придется добавить к классу игрока какой-нибудь геттер,
> который вернет вам число фрагов.
Не вижу никакой проблемы в этом. Так и должно быть как по мне.

> Простейший пример — вы где-то в физическом движке пишете, что класс персонажа должен запомнить сколько раз он в кого-то попал.
Почему в физическом движке то? Это должна быть часть с игровой логикой. Физический движок вызовет лишь коллбек, что кто-то в кого-то попал. Дальше вступает в дело игровая логика, которая и запоминает в классе Player кто в кого попал. Сам же класс Player - это и есть один из классов игровой логики.

> (представьте, что у вас сотни подобных мест по всему коду)
Не вижу сотен мест, но вижу одно, обработка получения/нанесения урона.

> Очевидно, что нужен какой-то промежуточный класс (назовем ему LeaderboardRow), который содержит именно информацию о фрагах, и, в том числе, и ссылку на персонажа.
А вот это уже точно нафиг не нужно. Заводить сущность ради чего?

> Мы могли бы решить проблему, инвертировав зависимость — сделать абстрактный интерфейс, который, с одной стороны, будет реализовывать класс персонажа, а с другой, на него будет полагаться LeaderboardRow для получения имени.
Это еще более лютый мороз. Писать адаптер ради... да просто так. Имхо адаптеры должны писаться только тогда, когда у тебя уже есть 2 системы, и их надо как-то между собой связать. Если ты изначально пишешь системы несовместимыми, чтобы их приходилось связывать через адаптер - то поздравляю, ты говнокодер. Если же у тебя системы совместимы по интерфейсам, но ты сразу пишешь адаптер, который посто делает такие же вызовы куда-то, то снова поздравляю, ты говнокодер.

> В языках с поддержкой ООП нужна еще одна сущность, помимо классов: проекции.
>
> 1. Проекция мимикрирует под объект. Ее можно передать по ссылке, сослаться,
> присвоить. У нее есть методы и свойства.
> 2. Один класс может иметь сколько угодно проекций.
> 3. Проекция не может иметь своего собственного состояния.
> 4. Проекция имеет доступ ко всем данным и методам базового класса.
> 5. Проекция может расширять или сужать интерфейс класса.
> 6. Проекция не имеет своего жизненного цикла: ее нельзя создать и уничтожить.
> Проекция становится невалидной, только когда уничтожается базовый для нее
> объект.
Лол. Ты сейчас посути 1 в 1 описал интерфейсы. Только назвал их по другому.

#88
(Правка: 9:02) 8:54, 25 фев. 2020

MrShoor
> Не вижу никакой проблемы в этом. Так и должно быть как по мне.
Сейчас — да. У вас просто нет иного выбора. Проекция — это как бы зона для роста. Понятно, что можно выживать и без этого. Но с проекциями выживать будет удобнее.

> Почему в физическом движке то?
> Это должна быть часть с игровой логикой.
> Физический движок вызовет лишь коллбек, что кто-то в кого-то попал. Дальше
> вступает в дело игровая логика, которая и запоминает в классе Player кто в кого
> попал. Сам же класс Player - это и есть один из классов игровой логики.
Это же пример просто.

> Не вижу сотен мест, но вижу одно, обработка получения/нанесения урона.
Не нанесение урона. А мест в коде, которые нарушают принцип единственной ответственности. Таких сотни мест.

> А вот это уже точно нафиг не нужно. Заводить сущность ради чего?
Согласен. Только не понятно, о чем вопрос. Я же ясно написал, что чтобы соблюсти принцип единственной ответственности.

> Это еще более лютый мороз. Писать адаптер ради... да просто так. Имхо адаптеры должны писаться только тогда, когда у тебя уже есть 2 системы, и их надо как-то между собой связать. Если ты изначально пишешь системы несовместимыми, чтобы их приходилось связывать через адаптер - то поздравляю, ты говнокодер. Если же у тебя системы совместимы по интерфейсам, но ты сразу пишешь адаптер, который посто делает такие же вызовы куда-то, то снова поздравляю, ты говнокодер.
Мда. Вообще-то это правильный подход. В идеале так и нужно делать. Другой вопрос, что никто так не делает из за недостатка дисциплины.

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

> Лол. Ты сейчас посути 1 в 1 описал интерфейсы. Только назвал их по другому.
Нет. В интерфейсе нет реализации методов. Класс реализует интерфейс, но эта реализация все равно остается в конкретном классе. Когда речь идет о проекции, конкретная реализация остается в проекции, не в классе.
Кроме того, интерфейс только сужает интерфейс класса. Проекция может его расширять — вводить новые методы/свойства, которых нет в базовом классе.

#89
9:15, 25 фев. 2020

ychebotaev
> Не нанесение урона. А мест в коде, которые нарушают принцип единственной
> ответственности. Таких сотни мест.
Каких мест сотни? У тебя должно быть единственное место в коде, которое обрабатывает нанесение урона. Зачем тебе сотня мест с обработкой нанесения урона?

> Я же ясно написал, что чтобы соблюсти принцип единственной ответственности.
Там всё соблюдено. Пишешь у класса Player метод DealDamage(...). Вызываешь только этот метод. В чем нарушение?

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

> Я только не понял, причем тут адаптер. Я описываю случай инверсии зависимостей
> — конкретные классы зависят от интерфейсов, а не от других конкретных классов.
Ну так в этом и роль адаптера. Обеспечить некий интерфейс.

> Нет. В интерфейсе нет реализации методов. Класс реализует интерфейс, но эта
> реализация все равно остается в конкретном классе. Когда речь идет о проекции,
> конкретная реализация остается в проекции, не в классе.
Ок, значит твой прототип - это паттерн "адаптер". Для чего придумывать какую-то новую сущность - я не понимаю.

Страницы: 15 6 7 837 Следующая »
ФлеймФорумПрограммирование