我知道fmap
的类型为(a -> b) -> f a -> f b
,其中f
是一个仿函数(根据仿函数的不同而做不同的事情)。我的基本问题是:给定一些调用fmap r x
,ghc如何确定函数f
是什么,只是给出了x
和r
的类型?
让我更准确地说明这一点。假设f
和f'
是仿函数,f a
= f' a
用于某种类型a
,但f b
和f' b
不同。如果r
的类型为a -> b
且x
的类型为f a
,则fmap r x
似乎有两种不同的可能结果:类型f b
和f' b
类型的东西。这种含糊不清是如何解决的?
第二个问题:我想通过制作一个奇怪的仿函数来测试这个问题 - 对于任何类型a
,可能需要[Int]
到a
,并且对函数执行愚蠢的操作。 ..但我显然没有找到合适的语法,允许我以这种方式指定仿函数。 (有data Newtype a = [Int]
这样的东西有效吗?看来我需要创建一个类型类名称才能使它成为仿函数的一个实例。)
Foo
这样Foo a
1}}是类似Int
的类型,已经存在。
答案 0 :(得分:2)
我认为您正在寻找的一般答案是Haskell类型使用“种类”进行组织,类似于类型。
这是Functor
类
class Functor f where
fmap :: (a -> b) -> f a -> f b
它并不明确,但这意味着f
是一个类型* -> *
的类型构造函数。只有类型的类型可以Functor
s。
这实际上是一个相当强烈的声明。这意味着任何Functor
必须在类型参数中具有参数。现在考虑你的陈述:
假设f和f'是仿函数,对于某些类型a,f a = f'a, 但是f b和f'b是不同的。
鉴于系统类型,这是不可能的。由于Functor
在其类型参数中具有参数,因此f a = f' a
隐含f = f'
,因此f b = f' b
。
我不完全确定你对“奇怪的仿函数”要求的是什么,但它听起来像Functor
类型类无法表达的东西。 IIRC Functor
只能在Hask上表达endofunctors;你可能需要一个不同的抽象,允许类别之间的仿函数。
答案 1 :(得分:1)
Haskell类型类基于一阶逻辑分辨率。类型变量上的类型类约束是谓词(如果您曾尝试在该逻辑系统中使用需要类型名称的类型类名称,则可能会看到错误消息,指示此情况)。
Haskell在整个程序中需要为每个(谓词,类型)对提供唯一的解决方案,因此您将无法在Int上创建两个不同的Functor实例。围绕这个的标准方法,例如在Monoid类中可以提供求和或产品的Monoid类,取决于你如何定义你想要使用的monoidal运算符,就是在具体类型上提供newtype
包装器你希望这个类有不同的实例。
因此,对于Monoid
,我们有newtype Sum a = Sum { getSum :: a }
和instance Num a => Monoid (Sum a)
用于总和monoid,newtype Product a = Product { getProduct :: a }
和instance Num a => Monoid (Product a)
用于产品monoid。
请注意,由于type
仅为类型创建别名,因此仅为类型提供多个类实例是不够的。 newtype
声明就像type
一样,它不会为新类型生成任何额外的运行时结构,但它与type
不同,因为它创建了一个新类型而不是{{1}}而不是类型别名。
答案 2 :(得分:0)
这取决于你传递的参数。例如,列表是仿函数,Maybe
main = do
putStrLn $ show (double [1..5])
putStrLn $ show (double (Just 3))
putStrLn $ show (double Nothing)
double :: (Functor f, Num a) => f a -> f a
double = fmap (*2)
*Main> main
[2,4,6,8,10]
Just 6
Nothing
此double
函数适用于持有Num
的任何仿函数。
答案 3 :(得分:0)
“假设f
和f'
是仿函数,f a
= f' a
适用于某种类型a
,但f b
和{{1}是不同的。“
这没有多大意义。 f' b
和f
相同,或者不相同。你似乎建议某种中间状态,它根据参数类型而变化;这不可能发生。
“如果f'
的类型为r
且a -> b
的类型为x
,则f a
似乎有两种不同的可能结果:某种类型{ {1}}以及fmap r x
类型的内容。这种歧义如何解决?“
f b
来自哪里?上述签名中没有任何内容提及它。由于f' b
的类型为f'
,因此x
的结果必须包含以f a
开头的某种类型 - 在本例中为fmap
,因为{{1} }}。这完全是明确的。 f
的结果总是与您开始使用的是同一个仿函数。