如何避免为执行模式匹配的函数编写样板代码? [英] How can I avoid writing boilerplate code for functions performing pattern matching?

查看:78
本文介绍了如何避免为执行模式匹配的函数编写样板代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此回复另一个问题,给出了一些Haskell代码草图,它使用包装函数将一些代码用于对命令行参数进行语法检查。以下是我试图简化的部分代码:

  takesSingleArg ::(String  - > IO()) - > [字符串]  - > IO()
takesSingleArg act [arg] = act arg
takesSingleArg _ _ = showUsageMes​​sage

takesTwoArgs ::(String - > String - > IO()) - > ; [字符串] - > IO()
takesTwoArgs act [arg1,arg2] = act arg1 arg2
takesTwoArgs _ _ = showUsageMes​​sage

有没有办法(可能使用模板Haskell ?)来避免为每个参数编写额外的函数?理想情况下,我希望能够写出类似于(我正在创建此语法)

  generateArgumentWrapper <2, showUsageMes​​sage> 

然后扩展为

  \fn args  - >案例参数
[a,b] - > fn a b
_ - > showUsageMes​​sage

理想情况下,我甚至可以为 generateArgumentWrapper设置可变数量的参数元函数,以便我可以做

pre $ generateArgumentWrapper< 2,asInt,asFilePath,showUsageMes​​sage>

然后扩展为

  \fn args  - >案例参数
[a,b] - > fn(asInt a)(asFilePath b)
_ - > showUsageMes​​sage

是否有人知道实现此目的的方法?将命令行参数( [String] )绑定到任意函数将是一种非常简单的方法。或者可能有一个完全不同的,更好的方法?

解决方案

Haskell具有多变量函数。想象一下,你有一个类似于

  data Act = Run(String  - > Act)| Res(IO())

有一些功能可以做你想做的事

  runAct(Run f)x = fx 
runAct(Res _)x =错误错误的函数类型

takeNargs'0(Res b)_ = b
takeNargs'0(Run _)_ = error错误的函数类型
takeNargs'n act(x:xs)= takeNargs'(n-1) (runAct act x)xs
takeNargs'_ _ [] = error列表不够长



现在,你所需要的只是把函数编入这个 Act 类型。您需要一些扩展名

  { - #LANGUAGE FlexibleInstances,FlexibleContexts# - } 

然后你可以定义

  class Actable a where 
makeAct :: a - > Act
numberOfArgs :: a - > Int

实例Actable(String - > IO())其中
makeAct f =运行$ Res。 f
numberOfArgs _ = 1

实例可实行(b - > c)=> Actable(String - >(b - > c))其中
makeAct f =运行$ makeAct。 f
numberOfArgs f = 1 + numberOfArgs(f)

现在您可以定义

  takeNArgs n act = takeNargs'n(makeAct act)

这样可以更容易地定义您的原始函数

  takesSingleArg ::字符串 - > IO()) - > [字符串]  - > IO()
takesSingleArg = takeNArgs 1

takesTwoArgs ::(String - > String - > IO()) - > [字符串] - > IO()
takesTwoArgs = takeNArgs 2

但我们可以做得更好

  takeTheRightNumArgs f = takeNArgs(numberOfArgs f)f 



令人惊讶的是,这项工作(GHCI)

  * Main> takeTheRightNumArgs putStrLn [hello,world] 
hello
* Main> takeTheRightNumArgs(\xy - > putStrLn x>> putStrLn y)[hello,world]
hello
world






编辑:上面的代码比需要的复杂得多。真的,所有你想要的是

  class TakeArgs a其中
takeArgs :: a - > [字符串] - > IO()

实例TakeArgs(IO())其中
takeArgs a _ = a

实例TakeArgs a => TakeArgs(String - > a)其中
takeArgs f(x:xs)= takeArgs(fx)xs
takeArgs f [] =错误列表结尾


In this response to another question, a little Haskell code sketch was given which uses wrapper functions to factor out some code for doing syntax checking on command line arguments. Here's the part of the code which I'm trying to simplify:

takesSingleArg :: (String -> IO ()) -> [String] -> IO ()
takesSingleArg act [arg] = act arg
takesSingleArg _   _     = showUsageMessage

takesTwoArgs :: (String -> String -> IO ()) -> [String] -> IO ()
takesTwoArgs act [arg1, arg2] = act arg1 arg2
takesTwoArgs _   _            = showUsageMessage

Is there a way (maybe using Template Haskell?) to avoid having to write extra functions for each number of arguments? Ideally, I'd like to be able to write something like (I'm making this syntax up)

generateArgumentWrapper<2, showUsageMessage>

And that expands to

\fn args -> case args of
                 [a, b] -> fn a b
                 _      -> showUsageMessage

Ideally, I could even have a variable number of arguments to the generateArgumentWrapper meta-function, so that I could do

generateArgumentWrapper<2, asInt, asFilePath, showUsageMessage>

And that expands to

\fn args -> case args of
                 [a, b] -> fn (asInt a) (asFilePath b)
                 _      -> showUsageMessage

Is anybody aware of a way to achieve this? It would be a really easy way to bind command line arguments ([String]) to arbitrary functions. Or is there maybe a totally different, better approach?

解决方案

Haskell has polyvariadic functions. Imagine you had a type like

data Act = Run (String -> Act) | Res (IO ())

with some functions to do what you want

runAct (Run f) x = f x
runAct (Res _) x = error "wrong function type"

takeNargs' 0 (Res b) _ = b
takeNargs' 0 (Run _) _ = error "wrong function type"
takeNargs' n act (x:xs) = takeNargs' (n-1) (runAct act x) xs
takeNargs' _ _ [] = error "not long enough list"

now, all you you need is to marshal functions into this Act type. You need some extensions

{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}

and then you can define

class Actable a where
  makeAct :: a -> Act
  numberOfArgs :: a -> Int

instance Actable (String -> IO ()) where
  makeAct f = Run $ Res . f
  numberOfArgs _ = 1

instance Actable (b -> c) => Actable (String -> (b -> c)) where
  makeAct f = Run $ makeAct . f
  numberOfArgs f = 1 + numberOfArgs (f "")

now you can define

takeNArgs n act = takeNargs' n (makeAct act) 

which makes it easier to define your original functions

takesSingleArg :: (String -> IO ()) -> [String] -> IO ()
takesSingleArg = takeNArgs 1

takesTwoArgs :: (String -> String -> IO ()) -> [String] -> IO ()
takesTwoArgs = takeNArgs 2

But we can do even better

takeTheRightNumArgs f = takeNArgs (numberOfArgs f) f

Amazingly, this works (GHCI)

*Main> takeTheRightNumArgs putStrLn ["hello","world"]
hello
*Main> takeTheRightNumArgs (\x y -> putStrLn x >> putStrLn y)  ["hello","world"] 
hello
world


Edit: The code above is much more complicated than it needs to be. Really, all you want is

class TakeArgs a where
   takeArgs :: a -> [String] -> IO ()

instance TakeArgs (IO ()) where
   takeArgs a _ = a

instance TakeArgs a => TakeArgs (String -> a) where
   takeArgs f (x:xs) = takeArgs (f x) xs
   takeArgs f [] = error "end of list"

这篇关于如何避免为执行模式匹配的函数编写样板代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆