如何在 Haskell 中 "overload" 美元运算符

How to "overload" dollar operator in Haskell

首先,如果这个问题已经被问到,我很抱歉,我找不到合适的英文术语来表达我的意思。

我想知道 Haskell 中是否有任何类型 Class 表示函数应用程序以便为不同数据类型定义多个行为。

使用 Graphics.X11.Xlib 包,我遇到了许多要求完全相同参数的不同函数。所以我的想法是将这些函数打包成一个元组(因为它们的 return 类型不一样),并一次性将参数全部喂给它们。像这样:

import Graphics.X11.Xlib

main = do
  display <- openDisplay ":0"
  let dScreen = defaultScreen display
      (black, white, cMap) =
      -- here is where the "parameter dispatch" is needed
      (blackPixel, whitePixel, defaultColormap)  display dScreen

  -- computation
  return ()

我没有找到任何东西,所以我决定创建这种类型 Class:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-}
import Graphics.X11.Xlib

class Dispatch f x y | x y -> f where
  dsp :: f -> x -> y
instance Dispatch (a -> b, a -> c, a -> d) a (b, c, d) where
  dsp (f, g, h) x = (f x, g x, h x)

main = do
  display <- openDisplay ":0"
  let dScreen = defaultScreen display
      (black, white, cMap) =
      -- here is where the "parameter dispatch" is needed
      (blackPixel, whitePixel, defaultColormap) `dsp` display `dsp` dScreen

  -- computation
  return ()

它工作正常,通过为不同的元组大小乘以实例,可以根据需要的值简单地从 "function tuple" 添加或删除函数,并且代码仍然可以编译。

但是如果没有这个变通办法,有什么办法可以做到吗? 我尝试使用 Control.ApplicativeControl.Arrow,但多个参数函数的结果并不理想。

到目前为止我最好的尝试是:(,) <$> blackPixel <*> whitePixel

这不是一个真正的答案,但可以通过以下方式将此想法扩展为真正的“$ 运算符的重载”:

{-# LANGUAGE TypeFamilies, FlexibleInstances #-}

import Prelude hiding (($))

infixr 0 $

class Dispatch f where
  type Argument f :: *
  type Result f :: *
  ($) :: f -> Argument f -> Result f

instance Dispatch (a -> b) where
  type Argument (a->b) = a
  type Result (a->b) = b
  f $ x = f x

instance (Dispatch x, Dispatch y, Argument x ~ Argument y)
            => Dispatch (x,y) where
  type Argument (x,y) = Argument x
  type Result (x,y) = (Result x, Result y)
  (f,g) $ a = (f $ a, g $ a)

instance ( Dispatch x, Dispatch y, Dispatch z
         , Argument x ~ Argument y, Argument y ~ Argument z )
            => Dispatch (x,y,z) where
  type Argument (x,y,z) = Argument x
  type Result (x,y,z) = (Result x, Result y, Result z)
  (f,g,h) $ a = (f $ a, g $ a, h $ a)

main :: IO ()
main = do
   print $ ((\x -> ((2*x+),(3*x+)), (**), logBase) $ 2) $ 4
((8.0,10.0),16.0,2.0)

您可以使用 liftA 函数族将许多函数应用于单个参数。嵌套它们以将许多函数应用于许多参数。所以:

(b, w, m) = liftA3 (liftA3 (,,)) blackPixel whitePixel defaultColormap display dScreen