说函数foobar noo是我程序的基础。此外,尽管foo1, bar1foo2, bar2仍然具有相同的输入和输出类型,但是可以在不同的场景(foo1foo2等)中以不同的方式实现这些功能。根据某些输入或配置,该程序在某些情况下使用foo1, bar1,而在另一种情况下使用foo2, bar2

我本可以如上所述定义它们,将后缀(1,2,3 ..)附加到foo, bar, noo。但是,这并不好,因为后缀可能很长。也不允许foo1bar1(与bar2进行特殊绑定)。

一种替代方法是将每个方案都视为单独的Module。现在,每种情况的foo, bar, noo都很好地结合在一起,避免了丑陋的后缀。但是,当每个Module一个文件时,这会引入许多文件。这种方法的另一个缺点是,即使它们确实共享一些相似性(例如,三个功能),这些Modules也是分开完成的。

会喜欢typeclass解决方案,但我不会想到,因为不同场景的不同foo具有相同的输入和输出。

我想知道是否有针对此问题的Haskell最佳实践,可以避免上述方法的上述缺点。

foo1 :: Double -> Double
bar1 :: Int -> Int
noo1 :: [Int] -> [Int]

foo2 :: Double -> Double
bar2 :: Int -> Int
noo2 :: [Int] -> [Int]

...

foo9 :: Double -> Double
bar9 :: Int -> Int
noo9 :: [Int] -> [Int]

编辑:我想这与讨论有关,以解释我将如何通过Java Interface处理它(有关Java interfaceHaskell typeclass的一些不错的讨论,但可以在this posthere上找到。)Java interface and class可能很复杂很多情况下,但是这里的重载实际上是简洁的。
interface Scenario {
  double     foo(double d);
  int        bar(int i);
  Array<int> noo(Array<int> a);
}

class UseScenario {
  void use(Scenario ss) {
    ss.foo(...);
    ss.bar(...);
    ss.noo(...);
  }
}

class S1 implements Scenario {
  double     foo(double d) {...};
  int        bar(int i) {...};
  Array<int> noo(Array<int> a) {...};
}

class S2 implements Scenario {
  double     foo(double d) {...};
  int        bar(int i) {...};
  Array<int> noo(Array<int> a) {...};
}

最佳答案

一种好的方法是将所有功能都放在一个数据类型中。然后,对于每个不同的策略,应具有该类型的不同值。最后,选择一个默认策略,并将实际功能链接到该默认策略(以方便使用)。例如:

module MyModule where


data Strategy  = Strategy {
    fooWithStrategy :: Double -> Double
  , barWithStrategy :: Int -> Int
  , nooWithStrategy :: [Int] -> [Int]
  }

defaultStrategy :: Strategy
defaultStrategy = Strategy {
    fooWithStrategy = (*2)
  , barWithStrategy = (+2)
  , nooWithStrategy = id
  }

foo :: Double -> Double
foo = fooWithStrategy defaultStrategy

bar :: Int -> Int
bar = barWithStrategy defaultStrategy

noo :: [Int] -> [Int]
noo = nooWithStrategy defaultStrategy

tripleStrategy :: Strategy
tripleStrategy = Strategy {
    fooWithStrategy = (*3)
  , barWithStrategy = (*3)
  , nooWithStrategy = \x -> x ++ x ++ x
  }

customAddStrategy :: Int -> Strategy
customAddStrategy n = Strategy {
    fooWithStrategy = (+ (fromIntegral n))
  , barWithStrategy = (+ n)
  , nooWithStrategy = (n :)
  }

这提供了许多有用的功能:
  • 可定制的策略(例如customAddStrategy)。您还可以混合和匹配策略,例如newStrat = defaultStrategy { nooWithStrategy = nooWithStrategy tripleStrategy, fooWithStrategy = (*4) }
  • 用户可以在运行时切换策略
  • 默认值(即foobarnoo)可用于
  • 库的新用户
  • 您或其他用户可以轻松扩展更多策略。
  • 09-08 11:03