[Haskell-cafe] Call for discussion: OverloadedLists extension

Heinrich Apfelmus apfelmus at quantentunnel.de
Fri Sep 28 15:11:47 CEST 2012


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




More information about the Haskell-Cafe mailing list