Overload built in function in Haskell

后端 未结 3 915
迷失自我
迷失自我 2021-01-20 03:05

In Haskell, how can one overload a built in function such as !!?

I originally was trying to figure out how to overload the built in function !!

相关标签:
3条回答
  • 2021-01-20 03:47

    In Haskell, nearly all operators are library-defined. Many of the ones you use the most are defined in the 'standard library' of the Prelude module that is imported by default. Gabriel's answer shows how to avoid importing some of those definitions so you can make your own.

    That's not overloading though, because the operator still just means one thing; the new meaning you define for it. The primary method that Haskell provides for overloading, i.e. using an operator in such a way that it has different implementations for different types, is the type class mechanism.

    A type class identifies a group of types that support some common functions. When you use those functions with a type, Haskell figures out the correct instance of the type class that applies to your usage and makes sure the correct implementation of the functions is used. Most type classes have just a few functions, some just one or two, that need to be implemented to make a new instance. Many of them offer a lot of secondary functions implemented in terms of the core ones as well, and you can use all of them with a type you make an instance of the class.

    It so happens that others have made types that behave quite a bit like lists, and so there's already a type class called ListLike. I'm not sure exactly how close your type is to a list, so it may not be a perfect fit for ListLike, but you should look at it as it will give you a lot of capability if you can make your type a ListLike instance.

    0 讨论(0)
  • 2021-01-20 03:49

    Hide the Prelude's (!!) operator and you can define your own (!!) operator:

    import Prelude hiding ((!!))
    
    (!!) :: MyType1 -> MyType2 -> MyType3
    x !! i = ... -- Go wild!
    

    You can even make a type class for your new (!!) operator if you prefer.

    0 讨论(0)
  • 2021-01-20 03:52

    You can't actually overload an existing non-typeclass function in Haskell.

    What you can do is define a new function in a new type class, which is general enough to encompass both the original function and the new definition you want as an overload. You can give it the same name as the standard function, and avoid importing the standard one. That means in your module you can use the name !! to get both the functionality of your new definition, and the original definition (the resolution will be directed by the types).

    Example:

    {-# LANGUAGE TypeFamilies #-}
    
    import Prelude hiding ((!!))
    import qualified Prelude
    
    class Indexable a where 
        type Index a
        type Elem a
        (!!) :: a -> Index a -> Elem a
    
    
    instance Indexable [a] where 
        type Index [a] = Int 
        type Elem [a] = a
        (!!) = (Prelude.!!)
    
    
    newtype MyType1 = MyType1 String
        deriving Show
    newtype MyType2 = MyType2 Int
        deriving Show
    newtype MyType3 = MyType3 Char
        deriving Show
    
    instance Indexable MyType1 where 
        type Index MyType1 = MyType2
        type Elem MyType1 = MyType3
        MyType1 cs !! MyType2 i = MyType3 $ cs !! i
    

    (I've used type families to imply that for a given type that can be indexed, the type of the indices and the type of the elements automatically follows; this could of course be done differently, but going into that in more detail is getting side-tracked from the overload question)

    Then:

    *Main> :t (!!)
    (!!) :: Indexable a => a -> Index a -> Elem a
    *Main> :t ([] !!)
    ([] !!) :: Int -> a
    *Main> :t (MyType1 "" !!)
    (MyType1 "" !!) :: MyType2 -> MyType3
    *Main> [0, 1, 2, 3, 4] !! 2
    2
    *Main> MyType1 "abcdefg" !! MyType2 3
    MyType3 'd'
    

    It should be emphasised that this has done absolutely nothing to the existing !! function defined in the prelude, nor to any other module that uses it. The !! defined here is a new and entirely unrelated function, which just happens to have the same name and to delegate to Prelude.!! in one particular instance. No existing code will be able to start using !! on MyType1 without modification (though other modules you can change can of course import your new !! to get this functionality). Any code that imports this module will either have to module-qualify all uses of !! or else use the same import Prelude hiding ((!!)) line to hide the original one.

    0 讨论(0)
提交回复
热议问题