2013-08-22 31 views
3

在Haskell中,如何重載內置函數,如!!Haskell中內置函數的過載

我最初試圖弄清楚如何重載內置函數!!以支持自己的數據類型。具體來說,!!的類型爲:

[a] -> Int -> a 

,我想保留它現有的功能,還能夠在那裏它的類型簽名看起來更像

MyType1 -> MyType2 -> MyType3 

我本來想做叫它這是因爲MyType1就像一個列表,我想使用!!運算符,因爲我的操作與從列表中選擇項目非常相似。

如果我重載了像+之類的東西,我可以將我的函數的一個實例添加到適用的類型類,但我不認爲這是一個選項。

我不確定我實際上甚至想重載這個函數,但我仍然對如何完成它感興趣。其實,如果超載運營商如!!甚至是一個好主意的意見,也將不勝感激。

+2

這是通過讓用戶導入兩個不同的'(!!)'並且至少有一個合格的,或者通過使用'(!)'來查找/索引來解決的。你不能真正重載Haskell中的任意函數/運算符。 – kqr

+0

請注意,您在技術上不能「超載」'!!',如果超載,則表示臨時的基於非類型類型的多態性。您可以像「超載」fmap,「<$>」或「>> =」那樣「重載」它,但它們必須被限制爲明確的類型類型(如monad或applicative或「list-like」)他們的類型簽名被推廣到整個類型類。你最好的選擇是爲類型類定義你自己的新的,泛化的'(!!)'的類型簽名,並且爲你想要的所有東西(!!)'定義實例。 –

回答

5

實際上,你不能在Haskell中重載現有的非類型類型函數。

你可以做的是在一個新的類型類中定義一個新的函數,該函數通用性足以涵蓋原始函數和想要作爲重載的新定義。您可以將其與標準功能同名,並避免導入標準功能。這意味着在您的模塊中,您可以使用名稱!!來同時獲得新定義的功能和原始定義(分辨率將由類型指示)。

實施例:

{-# 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 

(我使用類型系列暗示對可被索引給定類型,索引的類型和元素的自動類型如下:這當然可以以不同方式完成,但進入,在更詳細越來越扯到從過載問題)

然後:

*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' 

應該強調ŧ這對前奏中定義的現有!!函數沒有做任何事情,對其他任何使用它的模塊也沒有作用。這裏定義的!!是一個新的,完全不相關的函數,它恰好具有相同的名稱並在一個特定實例中委託給Prelude.!!。沒有現有的代碼將無需修改即可開始使用!!MyType1(儘管您可以更改的其他模塊當然可以導入新的!!以獲得此功能)。任何導入該模塊的代碼都必須模塊化,以限定!!的所有用途,或者使用相同的import Prelude hiding ((!!))行來隱藏原始模塊。

2

隱藏前奏的(!!)運營商,您可以定義自己的(!!)運營商:

import Prelude hiding ((!!)) 

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

你甚至可以使一個類型類新(!!)運營商,如果你喜歡。

8

在Haskell中,幾乎所有的操作符都是庫定義的。許多你使用最多的是在默認導入的Prelude模塊的'標準庫'中定義的。 Gabriel的答案顯示瞭如何避免導入一些定義,以便您可以自己創建。

雖然這不是重載,因爲操作員仍然只是意味着一件事;您爲它定義的新含義。 Haskell爲超載提供的主要方法,即使用運算符以使其具有不同類型的不同實現方式,即類型的類別機制。

一個類型類標識一組支持一些常用函數的類型。當您使用這些函數時,Haskell會根據您的使用情況找出適用於您的類型的正確的實例,並確保使用正確的函數實現。大多數類型類只有一些函數,有些只是一兩個,需要實現才能創建新實例。它們中的很多都提供了許多用核心實現的輔助功能,並且可以將它們全部用於創建類的實例。

恰巧其他人已經制作了類似於列表的類型,因此已經有一個名爲ListLike的類型類。我不確定你的類型到底是多麼接近列表,所以它可能不適合ListLike,但你應該看看它,因爲它會給你很多的功能,如果你可以讓你的類型成爲一個ListLike實例。