пятница, 3 июля 2026 г.

[prog.eiffel] Сохраню в склерозник пример работы с anchored-типами в Eiffel

Есть очень прикольный язык программирования -- Eiffel. А в нем есть очень прикольная фича -- заякоренные типы (anchored types). Давеча пришлось в одном из разговоров про нее вспомнить и набросать небольшой пример, чтобы проверить те или иные предположения.

У Eiffel-я есть он-лайн компилятор (что-то по типу godbolt, wandbox, ideone), но в нем нельзя просто так сохранить сделанное и поделиться публичной ссылкой. Поэтому помещу под кат код написанного примера, просто на память, а то вдруг еще раз потребуется.

В чем суть примера?

Есть базовый класс MESSAGE.

Есть базовый класс ENVELOPE, который хранит в себе MESSAGE. Но класс ENVELOPE написан с использованием MESSAGE в качестве якорного типа. Что позволяет создать наследника SIGNED_ENVELOPE, который хранит уже не просто MESSAGE, а SIGNED_MESSAGE. Но менять унаследованные из ENVELOPE методы не нужно, Eiffel сам разбирается с тем, что в SIGNED_ENVELOPE методы make и change_content получают уже не MESSAGE, а SIGNED_MESSAGE.

При этом Eiffel что-то может проверить в compile-time. Например, если у нас есть ссылка signed_env с типом SIGNED_ENVELOPE, то в вызов signed_env.change_content мы не может отдать просто ссылку на MESSAGE. Будет ошибка компиляции, компилятор ждет от нас SIGNED_MESSAGE (или наследника SIGNED_MESSAGE).

Но если у нас есть env типа ENVELOPE и мы env присвоили signed_env (т.е. теперь env ссылается на экземпляр SIGNED_ENVELOPE), то компилятор пропустит передачу обычного MESSAGE в env.change_content. Но ошибка будет диагностирована в run-time с выбросом исключения. Т.е. "обмануть" Eiffel не получится: то, что Eiffel не смог поймать в compile-time, он поймает в run-time.

Особенностью Eiffel-я является то, что каждый класс должен помещаться в отдельном файле. Поэтому сколько классов, столько и файлов.

Файл application.e

class
    APPLICATION

inherit
    ARGUMENTS_32

create
    make

feature {NONE} -- Initialization

    make
            -- Run application.
        local
            env: ENVELOPE
            env2: SIGNED_ENVELOPE
            msg: MESSAGE
        do
            create env.make (create {MESSAGE}.make("Hello"))
            env.examine_content 

            create env2.make (create {SIGNED_MESSAGE}.make("Bye"))
            env2.examine_content

            env := env2
            msg := create {SIGNED_MESSAGE}.make("222")
            env.change_content (msg)
            env.examine_content
        end

end

Файл msg.e

class
    MESSAGE
  
create
    make
    
feature
    body: STRING
    
    make(b: STRING)
        do
            body := b
        end
    
feature
    tell_about_itself
        do
            print("Message body is '")
            print(body)
            print("'%N")
        end
end

Файл envelope.e

class
    ENVELOPE
    
create
    make
    
feature
    msg: MESSAGE
    
feature
    make (m: like msg)
        do
            msg := m
        end
        
    change_content (m: like msg)
        do
            msg := m
        end
        
    examine_content
        do
            msg.tell_about_itself
        end
end

Файл signed_msg.e

class
    SIGNED_MESSAGE
    
inherit
    MESSAGE
        redefine
            tell_about_itself
        end
        
create
    make
    
feature
    tell_about_itself
        do
            print("SIGNED message body is '")
            print(body)
            print("'%N")
        end
end

Файл signed_envelope.e

class
    SIGNED_ENVELOPE

inherit
    ENVELOPE
        redefine
            msg
        end
    
create
    make
    
feature
    msg: SIGNED_MESSAGE

end

Комментариев нет: