3

This map is between a GType and an function that transforms an instance of that GType to an IO String. I am making a function that takes an element and gets a string representation of that element.

mapping =
    [ (Types.gTypeHTMLSourceElement, HTMLSourceElement.getSrc   )
    , (Types.gTypeHTMLObjectElement, HTMLObjectElement.getData  )
    , (Types.gTypeHTMLTimeElement  , HTMLTimeElement.getDateTime)
    , (Types.gTypeHTMLElement      , HTMLElement.getInnerText   )
    , ...
    ]

Gives this error:

Couldn't match type ‘HTMLObjectElement’ with ‘HTMLSourceElement’

I am using ghcjs-dom. How can I get this code to compile without errors? Am I approaching the problem in the right way?

If anyone can come up with answer better than Alec's answer I will accept their answer.

michaelmesser
  • 3,601
  • 2
  • 19
  • 42
  • I don't think you are approaching this the right way, but to get this to compile you'd probably need `RankNTypes` and add `mapping :: [(GType, forall a. IO (a -> String))]`. – Alec Aug 07 '16 at 04:21
  • @Alec any better way to approach it? If I switched based off node name, it would not be type safe. – michaelmesser Aug 07 '16 at 14:10
  • Rereading my comment, it should really have read `mapping :: [(GType, forall a . a -> IO String))]`. But regarding your question, I'm not sure what the best way would be... I guess you could make a typeclass `class Contents a where { getContents :: a -> IO String }` then make instances like `instance Contents HTMLSourceElement where { getContents = HTMLSourceElement.getSrc }`. – Alec Aug 08 '16 at 16:15
  • @Alec unfortunately an HTMLSourceElement my have the type HTMLElement. You cannot tell from the type what kind of element it is. – michaelmesser Aug 08 '16 at 16:57
  • I'm starting to see why you were initially trying what you were... How do the types for that work out? I'm not sure I understand how something can be `HTMLSourceElement` and `HTMLElement`. – Alec Aug 08 '16 at 17:17
  • @Alec You can use `toHTMLElement :: IsHTMLElement o => o -> HTMLElement` to convert from `HTMLSourceElement` to `HTMLElement` and `castToHTMLSourceElement :: GObjectClass obj => obj -> HTMLSourceElement` to unsafely convert `HTMLElement` to a `HTMLSourceElement`. You can check the type using `isA :: GObjectClass o => o -> GType -> Bool`. – michaelmesser Aug 08 '16 at 17:36
  • @Alec if only there was a `castToX :: GObjectClass obj => obj -> Maybe X` – michaelmesser Aug 14 '16 at 15:12

1 Answers1

0

I think the right way to approach the problem is to just make one toString function that does the branching based on the GType.

toString :: GObjectClass obj => obj -> IO String
toString obj | obj `isA` gTypeHTMLSourceElement = getSrc . castToHTMLSourceElement
             | obj `isA` gTypeHTMLObjectElement = getData . castToHTMLObjectElement
             | obj `isA` gTypeHTMLTimeElement = getDateTime . castToHTMLTimeElement
             | obj `isA` gTypeHTMLElement = getInnerText . castToHTMLElement
             | ...
Alec
  • 31,829
  • 7
  • 67
  • 114
  • Downvoter, would you care to justify the down vote? I'd be interested in feedback... – Alec Aug 12 '16 at 01:11