从递归类型生成函数

时间:2019-05-21 16:10:49

标签: haskell

假设在我的应用程序中,我有一组具有非常常规实现的功能(例如特定的日志记录功能)。我有类型

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}}。

2 个答案:

答案 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)

我不太确定我了解设置。我将做一些假设:

  1. data ShowMe a b = ShowMe a b
  2. 您想要一个新类,该类显示ShowMe的两个部分,并用空格隔开,因此例如showFn1showFn2可以通过分派给新类的方法。
  3. ShowFnsfnCollection是红色鲱鱼,是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'"