假设在我的应用程序中,我有一组具有非常常规实现的功能(例如特定的日志记录功能)。我有类型
data ShowFns =
{ showFn1 :: Int -> Bool -> Double -> String
, showFn2 :: Double -> Char -> String
}
这两个都可以简单地实现为
showFn1' :: Int -> Bool -> Double -> String
showFn1' a b c = show a <> " " <> show b <> " " <> show c
showFn2' :: Double -> Char -> String
showFn2' a b = show a <> " " <> show b
fnCollection :: ShowFns
fnCollection = ShowFns showFn1' showFn2'
但是,似乎此重复模式可以从递归类型派生,其中所有叶子类型都具有Show
实例。
我宁愿写:
fnCollection :: ShowFns
fnCollection = ShowFns toShowFn toShowFn
我认为这是可能的,因为这有点像Servant机械的工作方式,但是仆人却有点复杂,而且我无法将该机械复制到我的简单示例中。我已经尝试了一些使用开放类型族的公式,但是似乎无法使类型系统协调递归用例ShowMe
和基本用例{{1}}。
答案 0 :(得分:7)
实现可变参数功能的标准技巧如下:
DECLARE @myretValue int
EXEC GetIdOfUser 'raj', 'ahuja', @myretValue output
select @myretValue;
然后通常添加一个顶级助手:
{-# LANGUAGE FlexibleInstances #-}
class ShowMe a where showMeRaw :: [String] -> a
instance ShowMe String where showMeRaw = unwords . reverse
instance (Show a, ShowMe b) => ShowMe (a -> b) where
showMeRaw ss a = showMeRaw (show a : ss)
然后,您的showMe :: ShowMe a => a
showMe = showMeRaw []
和showFun1'
都只是对showFun2'
的调用。在ghci中试用:
showMe
答案 1 :(得分:4)
我不太确定我了解设置。我将做一些假设:
data ShowMe a b = ShowMe a b
ShowMe
的两个部分,并用空格隔开,因此例如showFn1
和showFn2
可以通过分派给新类的方法。ShowFns
和fnCollection
是红色鲱鱼,是XY问题或思想体系的一部分,可以完全忽略。在这些假设下,我将按照以下步骤进行操作:
class SpacedString a where spacedString :: a -> String
instance SpacedString () where spacedString _ = ""
instance (Show a, SpacedString b) => SpacedString (ShowMe a b) where
spacedString (ShowMe a b) = show a <> " " <> spacedString b
在ghci中试用:
> spacedString (ShowMe (3 :: Int) (ShowMe True (ShowMe 2.5 ())))
"3 True 2.5 "
> spacedString (ShowMe 2.5 (ShowMe 'a' ()))
"2.5 'a' "
如有必要,可以通过使class方法生成字符串列表并具有将unwords
和class方法结合在一起的单个顶级辅助函数来消除多余的尾随空格。
...但是,鉴于此版本中的样板代码少了很多,嵌套括号也少了很多,这一切似乎有点不合时宜。
> unwords [show (3 :: Int), show True, show 2.5]
"3 True 2.5"
> unwords [show 2.5, show 'a']
"2.5 'a'"