Does Haskell have return type overloading?

后端 未结 5 875
囚心锁ツ
囚心锁ツ 2020-12-05 03:09

Based on what I\'ve read about Haskell, and the experimentation I\'ve done with GHC, it seems like Haskell has return type overloading (aka ad hoc polymorphism). One example

5条回答
  •  隐瞒了意图╮
    2020-12-05 03:52

    I'd like to address one small part of your question:

    It also seems like the actual instance who's value we look at can be determined statically.

    This isn't really accurate. Consider the following wacky data type:

    data PerfectlyBalancedTree a
        = Leaf a
        | Branch (PerfectlyBalancedTree (a,a))
        deriving (Eq, Ord, Show, Read)
    

    Let's gawk at that type for a second first before we move on to the good bits. Here are a few typical values of the type PerfectlyBalancedTree Integer:

    Leaf 0
    Branch (Leaf (0, 0))
    Branch (Branch (Leaf ((0,0),(0,0))))
    Branch (Branch (Branch (Leaf (((0,0),(0,0)),((0,0),(0,0))))))
    

    In fact, you can visualize any value of this type as being an initial sequence of n Branch tags followed by a "we're finally done, thank goodness" Leaf tag followed by a 2^n-tuple of the contained type. Cool.

    Now, we're going to write a function to parse a slightly more convenient representation for these. Here's a couple example invocations:

    *Main> :t fromString
    fromString :: String -> PerfectlyBalancedTree Integer
    *Main> fromString "0"
    Leaf 0
    *Main> fromString "b(42,69)"
    Branch (Leaf (42,69))
    *Main> fromString "bbb(((0,0),(0,0)),((0,0),(0,0)))"
    Branch (Branch (Branch (Leaf (((0,0),(0,0)),((0,0),(0,0))))))
    

    Along the way, it will be convenient to write a slightly more polymorphic function. Here it is:

    fromString' :: Read a => String -> PerfectlyBalancedTree a
    fromString' ('b':rest) = Branch (fromString' rest)
    fromString' leaf = Leaf (read leaf)
    

    Now our real function is just the same thing with a different type signature:

    fromString :: String -> PerfectlyBalancedTree Integer
    fromString = fromString'
    

    But wait a second... what just happened here? I slipped something by you big time! Why didn't we just write this directly?

    fromStringNoGood :: String -> PerfectlyBalancedTree Integer
    fromStringNoGood ('b':rest) = Branch (fromStringNoGood rest)
    fromStringNoGood leaf = Leaf (read leaf)
    

    The reason is that in the recursive call, fromStringNoGood has a different type. It's not being called on to return a PerfectlyBalancedTree Integer, it's being called on to return a PerfectlyBalancedTree (Integer, Integer). We could write ourselves such a function...

    fromStringStillNoGood :: String -> PerfectlyBalancedTree Integer
    fromStringStillNoGood ('b':rest) = Branch (helper rest)
    fromStringStillNoGood leaf = Leaf (read leaf)
    
    helper :: String -> PerfectlyBalancedTree (Integer, Integer)
    helper ('b':rest) = Branch ({- ... what goes here, now? -})
    helper leaf = Leaf (read leaf)
    

    ... but this way lies an infinite regress into writing deeperly and deeperly nested types.

    The problem is that, even though we're interested in a monomorphic top-level function, we nevertheless cannot determine statically what type read is being called at in the polymorphic function it uses! The data we're passed determines what type of tuple read should return: more bs in the String means a deeper-nested tuple.

提交回复
热议问题