Проблема полиморфизма haskell в расширении модулей

В функциональном программировании и Haskell большой объем кода находится внутри функций.

Там с операторами if (или case):

Это часть кода какой-то функции:

case x of
    Apple i -> ...
    Orange i -> ...
    Tomato i -> ....

Это нормально работает, но у меня есть вопрос.

Как решить эту проблему.

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

Например, мясо, банан и все такое.

В Haskell в этой функции должны быть описаны все типы.

Но это невозможно, так как я не знаю, что добавят разработчики.

Поскольку в haskell у меня нет методов в объекте, я не могу применить метод к самому объекту.

Как решить эту проблему.

То, что вы здесь описываете, известно как «проблема выражения»: en.wikipedia.org/wiki/Expression_problem Для решения в Haskell см. people.cs.kuleuven.be/~tom.schrijvers/Research/talks/lhug2.p‌ df

Willem Van Onsem 31.10.2018 14:02

Я так понял вы типа все типы и что делать. Но это не решает проблемы. Я хочу, чтобы было добавлено больше типов извне модуля. Я создаю библиотеку, вы ее используете и можете добавлять больше типов. Может я что-то упускаю

user2693928 31.10.2018 14:08

ну, здесь мы добавляем дополнительный уровень «косвенности», чтобы люди могли подключать свой собственный тип с помощью ADT.

Willem Van Onsem 31.10.2018 14:09

Можете ли вы описать это простым кодом sudo, я не совсем понимаю монады, функторы и т. д. Можете ли вы описать основную идею - вы можете использовать код javacsript, если это уместно.

user2693928 31.10.2018 14:12
Apple, Orange и Tomato не являются типами; они являются конструкторами данных для типа Один. Разработчики не могут добавлять собственные конструкторы к существующему типу.
chepner 31.10.2018 14:35

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

chepner 31.10.2018 14:37

@ user2693928: Идея состоит в том, что вместо создания data Fruit = Apple | Orange | Tomato и т. д. вы создаете простые типы data Apple = Apple, data Orange = Orange и т. д., а затем определяете «тип суммы», поэтому, например, Apple + Orange, преимущество состоит в том, что вы можете определять функции над Типы просто. Таким образом, вы «поднимаете» проблему на один мета-уровень выше. Вы можете создавать отдельные объекты instance SomeOperation SomeType, и, определив instance SomeOperation (a + b), который выполняет «маршрутизацию», вам больше не нужно об этом заботиться. Сам Haskell позаботится о том, чтобы он был «маршрутизирован»

Willem Van Onsem 31.10.2018 14:37
2
7
75
1

Ответы 1

Если бы это был C++ / Java / etc.

Я предполагаю, что то, что вы подразумеваете под всем этим, - это то, что программисты OO могли бы решить с помощью базового класса и наследования:

class Food {
 private: int i;
 public: virtual void eat() = 0;
};

class Apple: public Food {public: void eat(){crunch(i);}};
class Orange: public Food {public: void eat(){squeek(i);}};
class Tomato: public Food {public: void eat(){splosh(i);}};
...
class Meat: public Food {public: void eat(){malm(i);}};  // added by other developer
...

Как правило, вы должны иметь в виду, что такие объектно-ориентированные классы отличаются от классов Haskell и вместо этого лучше представлены типами вариантов - как вы предполагаете в своем ответе.

Вариантное решение

data Food = Apple Int | Orange Int | Tomato Int
eat :: Food -> IO ()
eat (Apple i) = crunch i
eat (Orange i) = squeek i
eat (Tomato i) = splosh i

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

Однако противоположность «открытого мира», где добавляются различные новые типы объектов жестяная банка, определенно является реалистичным требованием, и это жестяная банка должно выполняться с помощью классов.

Решение класса типов / экзистенциалы

class Edible f where
  eat :: f -> IO ()

data Apple = Apple Int
instance Edible Apple where eat (Apple i) = crunch i
data Orange = Orange Int
instance Edible Orange where eat (Orange i) = squeek i
data Tomato = Tomato Int
instance Edible Tomato where eat (Tomato i) = splosh i
...
data Meat = Meat Int      --- added by other developer
instance Edible Meat where eat (Meat i) = malm i
...

Основное отличие от варианта типа состоит в том, что все разные типы Edible на самом деле имеют разные типы, поэтому вы не можете передавать список, содержащий, скажем, и Apple, и Orange. Ну, ты не можешь комп ...

В OO это тоже разные типы, однако там может быть ссылки на базовые классы, который фактически указывает на производный объект. Haskell не поддерживает это напрямую, но есть расширение GHC, которое поддерживает: это называется экзистенциальный тип и может быть записано либо

{-# LANGUAGE GADTs #-}
data Food where
  Food :: Edible f => f -> Food

или

{-# LANGUAGE ExistentialQuantification, UnicodeSyntax #-}
data Food = ∀ f . Edible f => Food f

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

Решение «объект простых данных»

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

data Food = Food {eat :: IO ()}

apple :: Int -> Food
apple i = Food $ crunch i
orange :: Int -> Food
orange i = Food $ squeek i
tomato :: Int -> Food
tomato i = Food $ splosh i
...
meat :: Int -> Food   -- added by other developer
meat i = Food $ malm i

Вы все равно можете добавить еще одно поле, в котором указано, с какой пищей вы имеете дело.

data Food = Food {foodVariety :: String, eat :: IO ()}

apple :: Int -> Food
apple i = Food "apple" $ crunch i

Если это антипаттерн, как бы я, например, мог бы выполнить эту задачу. У меня есть несколько функций формы и разные типы полей (TextField, DropdownField). Вы можете представить себе формы django, если хотите. Как мне создать другой тип поля, которого нет во фреймворке - например, поле автозаполнения или поле Google Map. Если я использую кейсы в функции, мне следует изменить код фреймворка. Как лучше всего заставить эту работу работать?

user2693928 01.11.2018 09:54

@ user2693928, что это «анти-шаблон», не означает, что вы никогда не должен использовать это. Такие виджеты, как TextField и т. д., Действительно часто лучше всего представлены ковариантно-полиморфными объектно-ориентированными объектами, а значит, в Haskell - экзистенциальными объектами. Но если класс, к которому принадлежат все эти объекты, имеет только такой метод, как Int -> IO (), тогда вообще нет никакого смысла создавать разные «подклассы» - просто сделайте «суперкласс» структурой data Foo = Foo {execFoo :: Int -> IO ()}.

leftaroundabout 01.11.2018 13:43

Хорошо, как решить описанную мной проблему. Как сделать так, чтобы новые типы можно было добавлять вне библиотеки. Итак, разработчик A может добавить поле Google Map с собственным поведением, разработчик B может добавить, например, ColorField?

user2693928 01.11.2018 13:45

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

leftaroundabout 01.11.2018 13:48

Вопрос именно в этом

user2693928 01.11.2018 14:33

Тогда это написано недостаточно ясно, чтобы это было ясно. Задайте еще один вопрос, посвященный конкретному варианту использования.

leftaroundabout 01.11.2018 14:42

Другие вопросы по теме