Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell中的重载内置函数_Haskell_Operator Overloading_Overloading - Fatal编程技术网

Haskell中的重载内置函数

Haskell中的重载内置函数,haskell,operator-overloading,overloading,Haskell,Operator Overloading,Overloading,在Haskell中,如何重载内置函数,如 我最初是想弄清楚如何重载内置函数以按自己的数据类型支持。具体来说,属于以下类型: [a] -> Int -> a 我想保留它现有的功能,但也能在它的类型签名看起来更像的地方调用它 MyType1 -> MyType2 -> MyType3 我最初想这样做是因为MyType1就像一个列表,我想使用运算符,因为我的操作与从列表中选择项目非常相似 如果我重载了+之类的内容,我可以将我的函数的一个实例添加到适用的类型类中,但我不认为这

在Haskell中,如何重载内置函数,如

我最初是想弄清楚如何重载内置函数
以按自己的数据类型支持。具体来说,
属于以下类型:

[a] -> Int -> a
我想保留它现有的功能,但也能在它的类型签名看起来更像的地方调用它

MyType1 -> MyType2 -> MyType3
我最初想这样做是因为MyType1就像一个列表,我想使用
运算符,因为我的操作与从列表中选择项目非常相似

如果我重载了
+
之类的内容,我可以将我的函数的一个实例添加到适用的类型类中,但我不认为这是一个选项

我甚至不相信我真的想重载这个函数了,但我仍然对如何实现它感兴趣。实际上,如果重载操作符,如
甚至是一个好主意,我们也将不胜感激

隐藏前奏曲的
(!!)
操作符,您可以定义自己的
(!!)
操作符:

import Prelude hiding ((!!))

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

如果愿意,您甚至可以为新的
(!!)
运算符创建一个类型类。

在Haskell中,几乎所有运算符都是库定义的。许多您使用最多的是在默认情况下导入的Prelude模块的“标准库”中定义的。Gabriel的回答说明了如何避免导入其中的一些定义,以便您可以自己创建定义

这并不是重载,因为操作符仍然只意味着一件事;你为它定义的新含义。Haskell为重载提供的主要方法是类型类机制,即使用运算符使其对不同类型有不同的实现

类型类标识支持某些公共函数的一组类型。当您将这些函数与类型一起使用时,Haskell会找出适用于您的用法的类型类的正确实例,并确保使用了正确的函数实现。大多数类型类只有几个函数,有些只有一个或两个,需要实现这些函数来创建新实例。它们中的许多还提供了许多根据核心函数实现的辅助函数,并且您可以将所有这些函数与您创建的类实例的类型一起使用


碰巧其他人已经创建了行为有点像列表的类型,因此已经有了一个名为
ListLike
的类型类。我不确定您的类型与列表有多接近,因此它可能不适合ListLike,但您应该看看它,因为如果您可以将您的类型设置为ListLike实例,它将为您提供很多功能。

您实际上不能在Haskell中重载现有的非typeclass函数

您可以做的是在一个新的类型类中定义一个新函数,它足够通用,可以包含原始函数和您想要作为重载的新定义。您可以将其命名为与标准函数相同的名称,并避免导入标准函数。这意味着您可以在模块中使用名称
获取新定义和原始定义的功能(解析将由类型决定)

例如:

{-# 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
(我用类型族来暗示,对于可以索引的给定类型,索引的类型和元素的类型会自动跟随;这当然可以用不同的方法来完成,但更详细地说,这是从重载问题中得出的结论)

然后:


应该强调的是,这对现有的<代码>前奏曲中定义的函数,以及使用该函数的任何其他模块。
是一个全新的、完全不相关的函数,它恰好具有相同的名称并委托给
Prelude在一个特定实例中。没有任何现有代码能够开始使用
MyType1
上的code>(尽管您可以更改的其他模块当然可以导入新的
!!
以获得此功能)。导入此模块的任何代码都必须通过模块验证
的所有使用
或使用相同的
导入前奏隐藏((!!))
行来隐藏原始的一行。

这在库中通过允许用户导入两个不同的
(!!)
并至少有一个合格的,或使用
(!)
进行查找/索引来解决。您不能在Haskell中重载任意函数/运算符。请注意,从技术上讲,您不能“重载”
,如果重载是指特殊的非类型类多态性。您可以像“重载”
fmap
>=
一样“重载”它,但它们必须被限制为显式类型类(如monad或applicative,或“list like”),并且它们的类型签名被泛化为整个类型类。您最好为typeclass定义您自己的新的、通用的
(!!)
的类型签名,并为所有您想要的
(!!)
定义实例。
*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'