Haskell функции полиморфного типа

Возможно ли в Haskell иметь функцию, которая может принимать полиморфный тип и возвращать полиморфный тип?

Например, мне нужна функция, которая принимает значение и возвращает Int, если значение имеет тип Foo, и строку, если оно имеет тип Bar.

data Foo = One | Two | Three deriving (Show, Read)
data Bar = This | That | TheOther deriving (Show, Read)

doSomething :: Either Foo Bar -> Either Int String
doSomething var = if (typeOf var) == Int then 123 else "string"

Возможно ли что-то подобное? Если нет, то как лучше всего маршрутизировать другую функцию на основе типа?


person Mark Karavan    schedule 25.11.2015    source источник
comment
Что вы пытаетесь решить с помощью этой функции?   -  person shree.pat18    schedule 25.11.2015
comment
Создание библиотеки, которая вычисляет вещи из теории музыки (гаммы, аккорды и т. д.). У меня есть функция, которая принимает гамму или аккорд и основную ноту, а затем возвращает список нот, который форматируется по-разному в зависимости от типа. Я полагал, что оптимизация этого, чтобы просто проиллюстрировать проблему, сделает его более привлекательным, но, вероятно, мне следовало просто включить функцию, которую я на самом деле использовал.   -  person Mark Karavan    schedule 25.11.2015
comment
Я думаю, что ваш пример был хорош - музыка, вероятно, скрыла бы детали / то, что важно для многих   -  person Random Dev    schedule 25.11.2015


Ответы (2)


сначала вещи, которые вы описываете, и подпись Either Int String кажутся несоответствующими - я попробую сначала то, что вы описываете (выберите тип вывода по типу ввода):

вы можете сделать что-то очень похожее на то, что, я думаю, вы пытаетесь сделать с семействами типов:

{-# LANGUAGE TypeFamilies #-}
module SO where

data Foo = One | Two | Three deriving (Show, Read)
data Bar = This | That | TheOther deriving (Show, Read)

class PolyMap k where
  type To k :: *
  polyMap :: k -> To k

instance PolyMap Foo where
  type To Foo = Int
  polyMap _ = 123

instance PolyMap Bar where
  type To Bar = String
  polyMap _ = "string"

пример:

λ> polyMap One
123
λ> polyMap That
"string"

какое-то объяснение

Я думаю, что вы хотите иметь сопоставления типов/функции (нет проверки во время выполнения с typeOf из коробки, и вместо этого вы получите хорошую поддержку проверки типов), и есть в основном два способа сделать это ( я в курсе)

Оба дают вам (среди прочего) средства, чтобы каким-то образом сказать: посмотрите, если я получу тип A, я могу сказать, каким должен быть некоторый связанный тип B (Foo -> Int и Bar -> String)

Это глубокая тема (пограничные зависимые типы;)), но я думаю, что семейства типов с классами несложно понять.

Идея, которую я использовал, состоит в том, чтобы иметь класс PolyMap, который предоставляет функцию polyMap (вы можете назвать его как угодно — doSomething, как угодно), и там тип вывода зависит от типа ввода с использованием отображения To k, которое Int для Foo и String для Bar, как указано в объявлениях экземпляров.


другой для вашей подписи еще проще:

doSomething :: Either Foo Bar -> Either Int String
doSomething (Left _) = Left 123
doSomething (Right _) = Right "string"

пример:

λ> doSomething (Left One)
Left 123
λ> doSomething (Right That)
Right "string"
person Random Dev    schedule 25.11.2015
comment
Что помогает. Решение, которое вы предложили для моей сигнатуры типа, похоже, максимально близко к выполнению действий, зависящих от типа, внутри функции, но оно требует явного знания того, на какой стороне Либо оно происходит. Кажется стандартной практикой делать их в объявлениях экземпляров. - person Mark Karavan; 25.11.2015
comment
если вы хотите Either, вам придется иметь дело с Left/Right - так же, как с Maybe и Just/Nothing - person Random Dev; 25.11.2015

Вы можете поместить doSomething в класс типов с ключами как для домена, так и для типов кодомена, с функциональной зависимостью от домена к кодомену:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances  #-} -- if one of your `to` really is `String`
class DoSomething from to | from -> to where
    doSomething :: from -> to

instance DoSomething Foo Int where -- ...
instance DoSomething Bar String where -- ...

По сравнению с решением, основанным на семействе типов, это имеет то преимущество, что если кодовый домен также однозначно определяет домен, вы можете добавить еще одну функциональную зависимость to -> from. Я не думаю, что семейства шрифтов предлагают хороший способ моделирования этого.

person Cactus    schedule 25.11.2015
comment
скоро: семейства инъективных типов ;) - конечно, в этом особом случае вы, вероятно, не нужно (или даже не хочется) - трудно сказать, исходя из того, что мы знаем - person Random Dev; 25.11.2015
comment
@Carsten: ну конечно. Однако я должен сказать, что не знал, что это также будет поддерживать открытые семьи. - person Cactus; 25.11.2015