2016-04-19 14 views
1
{-# LANGUAGE TemplateHaskell  #-} 

import Control.Lens 

data Fruit = Fruit 
    { _fruitColor :: String 
    } 

$(makeClassy ''Fruit) 

data Fruits = 
    Banana Int Fruit 
    | Strawberry String Fruit 

printer :: HasFruit s => s -> IO() 
printer f = putStrLn $ f ^. fruitColor 

sample :: IO() 
sample = do 
    printer $ Fruit "yellow" 
    printer $ Banana 5 $ Fruit "yellow" 

Ich habe eine Kernproduktart, Obst. Ich möchte Datentypen haben, die alle den Kernprodukttyp Obst enthalten, aber in der Lage sein sollen, sie durch eine Summenart, Früchte, darzustellen. Mit dem Sum-Typ "Früchte" möchte ich auf Komponenten des Hauptprodukttyps "Obst" zugreifen können. Nämlich, ich möchte, dass Typen wie Banana und Strawberry eine Instanz von HasFruit sind.Objektive mit Summenarten (die Produkttypen haben)

Gibt es einen einfachen Weg, um Früchte HasFruit zu instanziieren? Gibt es ein besseres Muster, um dies darzustellen - Kernprodukttyp in einer Summenart?

+1

Ich weiß nicht, ob das Objektiv Paket eine solche Instanz ableiten kann, aber man kann es sich nur schreiben: 'Beispiel HasFruit Früchte wo Obst k (Banana x f) = Banana x <$> k f; Frucht k (Erdbeere x f) = Erdbeere x <$> k f'. Es sollte angemerkt werden, dass "Banana" und "Strawberry" nicht * Typen sind - und keine Instanzen von Klassen sein können, weil sie keine Typen sind. Sie sind Konstruktoren, die einen Wert vom Typ 'Frucht' zurückgeben. – user2407038

Antwort

1

Es würde mehr Sinn machen, so etwas zu tun:

data FruitType = Banana Int | Strawberry String 

data Fruits = Fruits { _fruitsFruitType :: FruitType, _fruitsFruit :: Fruit } 

makeFields ''Fruits 

-- `Fruits` now instantiates `HasFruit` 

Vielleicht wollen Sie auch Prismen für FruitType machen. Prismen sind im Wesentlichen Traversierungen in verschiedene Teile eines Summentyps, außer dass sie auch verwendet werden können, um sie zu konstruieren.

makePrisms ''FruitType 

-- double the int if the argument is a `Banana` 

doubleBanana = fruitType._Banana *~ 2