Try with -XExtendedDefaulingRules.
On Mon, Nov 16, 2009 at 6:33 AM, Mark Lentczner <[email protected]> wrote: > I'm looking for a good way to handle a library interface that accepts both > strings and numbers in particular argument positions: > > Start with the following definitions. I've defined ResourceTree as a class > here since the details don't matter. > >> data Segment = Key String | Index Int >> deriving (Eq, Show) >> >> type Path = [Segment] >> >> class ResourceTree a where >> lookupPath :: Path -> a -> Maybe String > > What I'm after is to make the following code, representative of intended > client usage to work: > >> examples :: (ResourceTree a) => a -> [Maybe String] >> examples r = [ >> r `at` "status", >> r `at` 7, >> r `at` "part" ./ "sku", >> r `at` "items" ./ 2, >> r `at` 7 ./ "name", >> r `at` 7 ./ 9 >> ] > > The first way I thought to do this was with type classes: > >> class Segmentable a where { toSegment :: a -> Segment } >> instance Segmentable Segment where { toSegment = id } >> instance Segmentable String where { toSegment = Key } >> instance Segmentable Int where { toSegment = Index } >> >> class Pathable a where { toPath :: a -> Path } >> instance Pathable Path where { toPath = id } >> instance Pathable String where { toPath s = [ Key s ] } >> instance Pathable Int where { toPath i = [ Index i ] } >> >> (./) :: (Segmentable a, Pathable b) => a -> b -> Path >> a ./ b = toSegment a : toPath b >> infixr 4 ./ >> >> at :: (ResourceTree a, Pathable b) => a -> b -> Maybe String >> a `at` b = lookupPath (toPath b) a >> infix 2 `at` > > This works great for all uses in the client code where the type of the > numeric arguments are known or otherwise forced to be Int. However, when used > with numeric constants (as in the function example above), it fails due to > the way that numeric constants are defined in Haskell. For example, the > constant 9 in example results in this error: > > Ambiguous type variable `t4' in the constraints: > `Pathable t4' arising from a use of `./' at Test.hs:48:15-20 > `Num t4' arising from the literal `9' at Test.hs:48:20 > Probable fix: add a type signature that fixes these type variable(s) > > I suppose that even though there is only one type that is both an instance of > Num and of Pathable (Int), that can't be deduced with certainty. > > In the client code, one could fix this by typing the constants thus: > >> r `at` (7::Int) ./ (9::Int) > > But to me that makes a hash out of the concise syntax I was trying to achieve. > > Also, this code requires both FlexibleInstances and TypeSynonymInstances > pragmas (though the later requirement could be worked around.), though I'm > lead to understand that those are common enough. I think also that, these are > only needed in the library, not the client code. > > The other way I thought to do this is by making Path and Segment instances of > Num and IsString: > >> instance Num Segment where { fromInteger = Index . fromInteger } >> instance IsString Segment where { fromString = Key . fromString } >> instance Num Path where { fromInteger i = [ Index $ fromInteger i ] } >> instance IsString Path where { fromString s = [ Key $ fromString s ] } >> >> (./) :: Segment -> Path -> Path >> a ./ b = a : b >> infixr 4 ./ >> >> at :: (ResourceTree a) => a -> Path -> Maybe String >> a `at` b = lookupPath b a >> infix 2 `at` > > This works but has two downsides: 1) Segment and Path are poor instances of > Num, eliciting errors for missing methods and resulting in run-time errors > should any client code accidentally use them as such. 2) It requires the > OverloadedStrings pragma in every client module. > > Any comments on these two approaches would be appreciated, How to improve > them? Which is the lesser of two evils? > > On the other hand, I realize that many may object that intended interface > isn't very Haskell like. The data object I need to represent (ResourceTree) > comes from external input and really does have the strange "paths of strings > or integers" construction, I can't change that. And it is expected that much > client code will use constant paths to access and manipulate various parts of > such objects, hence the desire for a concise operator set that works with > constants. Given that there are actually several operations on ResourceTree > involving paths (where the operation requires the whole Path as a single > value), any thoughts on a more Haskell like construction? > > Thanks, > - MtnViewMark > > > Mark Lentczner > http://www.ozonehouse.com/mark/ > [email protected] > > > > _______________________________________________ > Haskell-Cafe mailing list > [email protected] > http://www.haskell.org/mailman/listinfo/haskell-cafe > _______________________________________________ Haskell-Cafe mailing list [email protected] http://www.haskell.org/mailman/listinfo/haskell-cafe
