Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

haskell - Type-threaded heterogenous lists and defaulting(?) with type families?

I'm working on a library where I want to define a recursive class that I've simplified here to:

{-# LANGUAGE MultiParamTypeClasses 
           , FlexibleInstances #-}

data Snoc st b c = Snoc (st b) (c -> b) 
data Top a = Top

class StackTo a st c  where
     runStack :: st c -> (c -> a)
instance StackTo a Top a where
     runStack _ = id
instance (StackTo a st b) => StackTo a (Snoc st b) c where
     runStack (Snoc st' cb) = runStack st' . cb

This lets me do, e.g.

*Main Data.Label> let f = runStack $ Snoc (Snoc Top fst) head :: [(a,x)] -> a
*Main Data.Label> f [('a',undefined)] 
'a'

But this seems to require careful use of type annotations, otherwise...

*Main Data.Label> let f = runStack $ Snoc (Snoc Top fst) head

<interactive>:1:1:
    No instance for (StackTo a0 Top b0)
      arising from a use of `runStack'
    Possible fix: add an instance declaration for (StackTo a0 Top b0)
    In the expression: runStack
    In the expression: runStack $ Snoc (Snoc Top fst) head
    In an equation for `it': it = runStack $ Snoc (Snoc Top fst) head

I think these are the same issues addressed in this question, but I'm having trouble adapting that solution here. Can I use type families or some other method to come up with a more user-friendly solution to my recursive continuation stack?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The answer to the linked question hides the following quite useful trick: generalize the instance head, and specialize in the instance context.

instance a ~ b => StackTo a Top b where
    runStack _ = id

When choosing an instance to use, GHC inspects available instance heads only -- not contexts -- and picks the one (if any) that matches what is currently known about a type. It will not specialize a type before making this choice, even if specializing would allow one or more of the available instance heads to match. Thus, the difference between the instance given here and the one in the question above is that this one is more general: this one applies whenever the middle type is Top, whereas yours applies only when the middle type is Top and we know enough about the two other types to know they're equal.

Yours will overlap with fewer other potential instances, but this will encourage the inference engine more strongly.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...