2014-01-08 2 views
4

Как преобразовать следующую реализацию Java в Haskell?Как сделать гетерогенный список в Haskell? (изначально в Java)

Основная цель здесь - иметь список, который содержит различные элементы, которые являются подтипом конкретного интерфейса.
Я попытался сделать версию Haskell ниже, но не смог выполнить свою задачу. Дело здесь xs имеет тип [Bar], а не Foo a => [a]

ли это, что Haskell не может это сделать, и мне кажется, по-другому?

Java

interface Foo { 
    void bar(); 
} 

public class Bar1 implements Foo { 
    @Override 
    public void bar() { 
     System.out.println("I am bar 1 class"); 
    } 
} 

public class Bar2 implements Foo { 
    @Override 
    public void bar() { 
     System.out.println("I am bar 2 class"); 
    } 
} 

public static void main(String[] args) { 
    // The major purpose here is having a list 
    // that contains elements which are sub-type of "Foo" 
    List<Foo> ys = new ArrayList<Foo>(); 

    Foo e1 = new Bar1(); 
    Foo e2 = new Bar2(); 

    ys.add(e1); 
    ys.add(e2); 

    for (Foo foo : ys) { 
     foo.bar(); 
    } 
} 

Haskell

class Foo a where 
    bar :: a -> IO() 

data Bar = Bar1 | Bar2 

instance Foo Bar where 
    bar Bar1 = print "I am Bar1" 
    bar Bar2 = print "I am Bar2" 

--xs :: Foo a => [a] 
xs :: [Bar] 
xs = [Bar1, Bar2] 

main :: IO() 
main = mapM_ bar xs 
+1

Ваша реализация в Haskell выглядит хорошо для меня. Я даже зашел так далеко и скажу, что правильный перевод: 'bar1 =" I'm Bar1 "; bar2 = "Я Bar2"; xs = [bar1, bar2]; main = mapM_ putStrLn xs' – kosmikus

+0

Фактически, дело в том, что для этого есть список элементов, реализующих определенный интерфейс. – Simon

+1

Да, но этот интерфейс (в данном случае) типа 'String', или, возможно,' IO String'. – kosmikus

ответ

16

Простой ответ: don't! Haskell is not an OO language, and it's not much good pretending it is and just trying to translate inheritance patterns to a mixture of type classes and ADTs.

Ваш List<Foo> в Java довольно существенно отличается от Foo a => [a] в Haskell: такая подпись фактически означает forall a . Foo a => [a]. a в основном является дополнительным аргументом функции, то есть его можно выбрать извне, какой именно экземпляр Foo используется здесь.

Совершенно противоположное в Java: там вы не имеете никакого контроля над тем, какие типы в списке вообще, только знаете, что они реализуют интерфейс Foo. В Haskell мы называем это экзистенциальным типом и вообще избегаем его, потому что это глупо. Хорошо, вы не согласны - извините, вы ошибаетесь!
... Нет, серьезно, если у вас есть такой экзистенциальный список, единственное, что вы можете сделать выполняет bar действие. Ну, тогда почему бы просто не просто положить это действие в список сразу! IO() действия - это значения, как и все остальное (они не являются функциями, в любом случае их можно также добавить в списки). Я бы написать программу

xs :: [IO()] 
xs = [bar Bar1, bar Bar2] 


Тем не менее, если вы абсолютно настаивают на том, что вы можете иметь экзистенциальные списки в Haskell, а также:

{-# LANGUAGE ExistentialQuantification #-} 

data AFoo = forall a. Foo a => AFoo a 

xs :: [AFoo] 
xs = [AFoo Bar1, AFoo Bar2] 

main = mapM_ (\(AFoo f) -> bar f) xs 

Как это стало довольно громкие слова: Я подчеркиваю, что стиль OO для некоторых приложений более удобен, чем функциональный стиль Haskell. И экзистенции имеют свои действительные варианты использования (хотя, как chunksOf 50, я предпочитаю писать их как GADTs). Только для множества проблем Haskell допускает гораздо более сжатые, мощные, общие, но во многом более простые решения, чем наследование «если у вас есть молот ...», которое вы использовали бы при программировании OO, поэтому перед использованием экзистенциальных вас должно получить правильное чувство для «родных» функций Haskell.


Да, я знаю, вы также можете сделать "типизированного динамический бросает" и т.д. в Java. В Haskell есть класс Typeable для такого рода вещей. Но вы можете также использовать динамический язык сразу, если вы примете такие пути.

+0

С помощью XConstraintKinds можно выполнить несколько интересных трюков. Но в целом вы правы в отношении бесполезности такого моделирования ООП. – user3974391

+1

GADT - это более красивый способ делать экзистенциальную количественную оценку, но список 'IO()' или тип данных, представляющий все, что вам нужно, лучше всего, как вы говорите, да. –

+4

Использование экзистенций не всегда ошибочно, это обычно неправильно. – augustss

9

Ваш перевод имеет важный недостаток. Если в вашей версии Java вы можете легко добавить Bar3, который также поддерживает интерфейс Foo, вы не можете легко достичь этого, не касаясь типа Bar в версии Haskell. Так что это скорее не версия, которую вы ищете.

В некотором роде вы ищете гетерогенный список. Otherquestions уже покрывают этот аспект.

Что вы действительно хотите, так это избавиться от необходимости типа класса вообще. Вместо того, чтобы иметь тип данных, представляющий поведение Foo:

data Foo = Foo { bar :: IO() } 

Тогда вы можете построить свой список объектов, удовлетворяющих интерфейс Foo как [Foo].

+1

Стоит отметить, что это очень похоже на экзистенциальные «данные Boxy = forall a. Classy a => Boxy a', так как классы типов в конечном итоге преобразуются в записи. – jozefg

7

Это возможно, но, возможно, не желательно. Существует такое языковое расширение, как «экзистенциальные типы», которое позволяет динамический полиморфизм.

Идея экзистенциального типа заключается в следующем:

data Foo = Foo a 

Обратите внимание, что тип переменной «а» не появляется на левой стороне ADT декларации. Простой пример возможной реализации динамического полиморфного список и карта функции:

{-# LANGUAGE UnicodeSyntax, Rank2Types, GADTs, ConstraintKinds #-} 

import Data.Constraint 

-- List datatype: 
data PList α where 
    Nil ∷ PList α 
    (:*) ∷ α a ⇒ a → PList α → PList α 

infixr 6 :* 

-- Polymorphic map: 
pmap ∷ (∀ a. α a ⇒ a → b) → PList α → [b] 
pmap _ Nil  = [] 
pmap f (a :* t) = f a : pmap f t 

main = let 
     -- Declare list of arbitrary typed values with overloaded instance Show: 
     l ∷ PList Show 
     l = "Truly polymorphic list " :* 'a' :* 1 :* (2, 2) :* Nil 
    in do 
     -- Map show to list: 
     print $ pmap show l 

Выход:

["\"Truly polymorphic list \"","'a'","1","(2,2)"] 

Другой пример использования этого метода:

class Show a ⇒ Named a where 
    name ∷ a → String 

instance Named Int where 
    name a = "I'm Int and my value is " ++ show a 

instance Named Char where 
    name a = "I'm Char and my value is " ++ show a 

main = let 
     -- Declare list of arbitrary typed values with overloaded instance Named: 
     l2 :: PList Named 
     l2 = 'a' :* (1 ∷ Int) :* Nil 
    in do 
     print $ pmap name l2 
     print $ pmap show l2 

Выход:

["I'm Char and my value is 'a'","I'm Int and my value is 1"] 
["'a'","1"] 
+0

Это был бы другой пример. Благодарю. Является ли 'Foo' именем« тип фата »? – Simon

+0

Нет, это не так. Это псевдокод для экзистенциального типа. – user3974391

Смежные вопросы