
Michael Snoyman wrote:
Heinrich Apfelmus wrote:
Michael Snoyman wrote:
Note that I wasn't necessarily advocating such a pragma. And a lot of my XML code actually *does* use two IsString instances at the same time, e.g.:
Element ("img" :: Name) (singleton ("href" :: Name) ("foo.png" :: Text)) [NodeComment ("No content inside an image" :: Text)]
In this particular case, would it make sense to use smart constructors instead?
The idea is that you can put the polymorphism in two places: either make the "output" polymorphic, or make the "input" polymorphic. The latter would correspond to a type
element :: (IsString name, IsString s, IsMap map) => name -> map name s -> [Element] element name map = Element (toName name) (toMap map)
One benefit would be that the function will accept any list as a map, not just list literals.
Just to clarify: this would be a *replacement* for OverloadedStrings usage, right? If used in conjunction with OverloadedStrings, we'd run into the too-much-polymorphism issue you describe in your initial email in this thread, since `element "foo'` would become `element (fromString "foo")` which would become `Element ((toName . fromString) "foo")`, and `toName . fromString` makes it ambiguous what the intermediate data type is.
Yes, indeed, it would be an alternative approach.
Assuming this is meant are a replacement, I see two downsides. Firstly, this would work for construction, but not for deconstruction. Currently, I can do something like:
handleList :: Element -> Element handleList (Element "ul" _ _) = ... handleList e = e
Good point. On the other hand, there is another extension, ViewPatterns, which solves the problem of pattern matching on abstract data types in full generality, allowing things like handleList (viewAsStrings -> Element "ul" _ _) = ... While more intrusive, the benefit of this extension is that a lot of other code could likely become neater as well.
The other is that we've only solved one specific case by providing a replacement function. In order to keep code just as terse as it is now, we'd have to provide a whole slew of replacement functions. For example, consider the code:
handleList (Element "ul" attrs _) = case Map.lookup "class" attrs of ....
If we get rid of OverloadedStrings, then we need to either provide a replacement `lookup` function which performs the conversion from String to Name, or change all lookup calls to explicitly perform that lookup.
Ah, I see. Since the Name type is abstract, I feel it's alright to add the polymorphism to functions like element , but Map.lookup is indeed a problem. One option would be to make a new type NameMap specifically for Name as key, but that seems a little overkill. The other option is to bite the bullet and add the conversion by hand Map.lookup (name "class") . In this case, I think I would go with a lightweight first option and simply give a new name to the Map.lookup combination and use the opportunity to sneak in some polymorphism. getAttribute name = Map.lookup (toText name) In my experience, turning all data types into abstractions works quite well, but I can see that you can't avoid an annoying conversion if you just want to use a quick Map.lookup . Best regards, Heinrich Apfelmus -- http://apfelmus.nfshost.com