将自同态应用于一对中不同类型的元素

sho*_*qie 5 haskell functional-programming

简而言之,我希望在 Haskell 中进行以下类型检查:

\n
both f (x, y) = (f x, f y)\n\nfoo :: ([Int], [Char])\nfoo = ([1], "a")\n\nbar :: ([Int], [Char])\nbar = both (concat . replicate 3) foo  -- intended: ([1, 1, 1], "aaa")\n
Run Code Online (Sandbox Code Playgroud)\n

我收到的错误是:

\n
\xe2\x80\xa2 Couldn\'t match type \xe2\x80\x98Char\xe2\x80\x99 with \xe2\x80\x98Int\xe2\x80\x99\n  Expected: ([Int], [Int])\n    Actual: ([Int], [Char])\n\xe2\x80\xa2 In the second argument of \xe2\x80\x98both\xe2\x80\x99, namely \xe2\x80\x98foo\xe2\x80\x99\n  In the expression: both (concat . replicate 3) foo\n  In an equation for \xe2\x80\x98bar\xe2\x80\x99: bar = both (concat . replicate 3) foo\n
Run Code Online (Sandbox Code Playgroud)\n

在添加以下类型注释后,我得到了它的编译both

\n
\xe2\x80\xa2 Couldn\'t match type \xe2\x80\x98Char\xe2\x80\x99 with \xe2\x80\x98Int\xe2\x80\x99\n  Expected: ([Int], [Int])\n    Actual: ([Int], [Char])\n\xe2\x80\xa2 In the second argument of \xe2\x80\x98both\xe2\x80\x99, namely \xe2\x80\x98foo\xe2\x80\x99\n  In the expression: both (concat . replicate 3) foo\n  In an equation for \xe2\x80\x98bar\xe2\x80\x99: bar = both (concat . replicate 3) foo\n
Run Code Online (Sandbox Code Playgroud)\n

但这个解决方案感觉并不完全令人满意,因为both看起来更通用,因此,例如both id (1, "a")也被接受。我可以从这里走得更远吗?

\n

lef*_*out 4

我认为这是最明智的版本:

\n
both :: \xe2\x88\x80 t a b . (\xe2\x88\x80 x . t x -> t x) -> (t a, t b) -> (t a, t b)\nboth f (x,y) = (f x, f y)\n
Run Code Online (Sandbox Code Playgroud)\n

您的列表示例是这种情况的特例t ~ [],但它也可以与其他容器一起使用,也可以间接与“不包含”值一起使用,因为您始终可以使用Identity\xe2\x80\x93 ,尽管这基本上是无用的,因为唯一的函数\xe2\x88\x80 x . x -> xid.

\n

  • 请注意,进一步推广到不同的输入和输出函子是合理的:`both :: forall sta b。(forall x.sx -> tx) -> (sa, sb) -> (ta, tb)`。 (3认同)