3

假设我正在创建一个处理文本文件的数据管道。我有以下类型和功能:

data A = A deriving (Show, Typeable)
data B = B deriving (Show, Typeable)
data C = C deriving (Show, Typeable)
data D = D deriving (Show, Typeable)

step1 :: A -> B
step2 :: B -> C
step3 :: C -> D

对于下面的每个函数 step{1..3},我希望能够从现有文件生成一个新文件,执行如下操作:

interact (lines . map (show . step . read) . unlines)

然后我希望能够将它们排列成一个图形(因此函数可以有多个输入)以实现以下目标:

  1. 我可以遍历数据结构来判断哪些函数向哪些其他函数提供输入

  2. 数据结构将在编译时进行类型检查,因此任何无效的排列都会引发类型错误。

我知道如何在没有 2 的情况下执行 1(给他们一个通用的类型类),并且我知道如何在没有 1 的情况下执行 2(只需使用 (.)),但我不确定如何同时执行这两项操作。有任何想法吗?TIA。

更新

AndrewC 的答案非常准确,并且让我大部分时间都在那里,因为我可以使用 Map 构建元数据(名称)图并同时单独对其进行类型检查,但是我还需要一些灵活的多态性。下面的示例解决了这个问题,但有两个严重的警告:

  1. 在第 4 步中,我不会被迫详尽地进行模式匹配。
  2. 这很乏味;可能有几十个这样的“BorC”风格的多态类型,除了启用多态之外什么都不做,甚至不是很安全(见 1)。我试图对类型类的成员进行模式匹配(带有andStep4Input的实例),但这不起作用(它说)。BCCouldn't match type B with C
data BorC = ItsB B | ItsC C

step4 :: BorC -> D
step4 x = case x of { ItsB b -> D; ItsC c -> D }

-- step1    step2
--    \       /
--   ItsB   ItsC
--     \   /
--     step4
4

1 回答 1

2

我可能误解了你的问题,但为什么不简单地用名字包装函数呢?
对于这种情况,这似乎是一种非常简单的方法,不需要太多的麻烦。

module NamedFunctions where

import Control.Category        -- so I can generalise (.)
import Prelude hiding ((.),id) -- don't worry, available from Category

将您编写的函数名称列表与函数本身一起存储:

data Fn name a b = Fn {names::[name],apply:: (a -> b)} 

当你展示一个函数时,只需展示你为到达那里而编写的内容:

instance Show name => Show (Fn name a b) where
  show f = show $ names f

定义(.)和的新版本($)

infixr 9 ...
(...) :: Fn name b c -> Fn name a b -> Fn name a c
f ... g = Fn (names f ++ names g) (apply f . apply g)

infixr 0 $$$
($$$) :: Fn name a b -> a -> b
f $$$ x = apply f x

并重新(.)开始使用命名函数

instance Category (Fn name) where
   (.) = (...)
   id = Fn [] id

您可以将您不关心的函数保留为未命名的,这样它们就不会对names列表产生影响。

name  n f = Fn [n] f
unnamed f = Fn []  f

您可以使用您喜欢的任何数据来命名每个函数,例如:

timesThree = name "(*3)" (*3)
addFour = name "(+4)" (+4)

step1 :: Fn Int A B 
step1 = name 1 undefined
step2 :: Fn Int B C 
step2 = name 2 undefined
step3 :: Fn Int C D 
step3 = name 3 undefined


*NamedFunctions> timesThree . addFour
["(*3)","(+4)"]
*NamedFunctions> timesThree . addFour $$$ 5
27
*NamedFunctions> step3.step2.step1
[3,2,1]
*NamedFunctions> step3.step2.step1 $$$ A
*** Exception: Prelude.undefined
于 2013-07-20T13:34:41.557 回答