如何在 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.Applicative
或 Control.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
首先,如果这个问题已经被问到,我很抱歉,我找不到合适的英文术语来表达我的意思。
我想知道 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.Applicative
或 Control.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