简体   繁体   中英

Overcoming limitation with polymorphic type in haskell

I have a type like so,

data AgentSpec a = AgentSpec 
                     { agent :: a
                     , events :: [ByteString] 
                     } deriving (Show)

and other types like so,

data Java = Java
data Php = Php

I am trying to create a function of type,

allEvents :: [AgentSpec a] -> [ByteString]
allEvents aspec = concatMap events aspec

But I am unable to create values of type [AgentSpec a]

If I do,

allEvents [AgentSpec Java ["event1, "event2"], AgentSpec Php ["event3, "event4"]]

it does not typecheck and understandably so. It doesn't type check because, AgentSpec Java is not AgentSpec a

So I understand why it does not work. But I do not know how to overcome this limitation without writing a lot of duplicate code.

One alternatve is to manually construct a list of type,

allEvents :: [ByteString]
allEvents = ["event1", "event2", "event3", "event4"].

But I feel like I'm simply rewriting things I've already modelled in my types. Is there any way to make use of my existing types to get what I want? Which is just a concatenated list of bytestrings of all the AgentSpecs

This code:

allEvents [AgentSpec Java ["event1, "event2"], AgentSpec Php ["event3, "event4"]]

doesn't compile because AgentSpec Java ["event1, "event2"] and AgentSpec Php ["event3", "event4"] are of different types, and lists in Haskell can only contain one type. An AgentSpec a [ByteString] can be created for any type a , but once created it can't be mixed with values of a different type.

I don't know what exactly you're modeling, but typically I'd recommend something like this:

data Language = Java | Php

data AgentSpec = AgentSpec 
                     { agent :: Language
                     , events :: [ByteString] 
                     } deriving (Show)

allEvents = [AgentSpec Java ["event1", "event2"], AgentSpec Php ["event3", "event4"]]

Based on your comment about writing a library, though, it doesn't sound like this would work. Can you elaborate on what you're trying to achieve?

There are some techniques to implement heterogenous lists in Haskell . One thing you can do is:

{-# LANGUAGE GADTs #-}

data SomeAgentSpec where
    SAS :: AgentSpec a -> SomeAgentSpec

allEvents :: [SomeAgentSpec] -> [ByteString]
allEvents aspec = concatMap ev aspec
    where ev (SAS a) = events a

then,

\> let a = AgentSpec Java ["1.", "java"]
\> let b = AgentSpec Php  ["2.", "php" ]
\> allEvents [SAS a, SAS b]
["1.","java","2.","php"]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM