Haskell设计了与类似功能集相关联的多个模块的最佳实践

Cau*_*ity 4 java haskell design-patterns interface typeclass

说出功能foo,bar noo是我程序中的基础知识.此外,这些功能可以在不同的不同的方式来实现的场景(foo1, bar1, foo2, bar2等),虽然foo1foo2仍然具有相同的输入和输出类型.根据一些输入或配置,程序foo1, bar1在某些情况下使用,而在另一种情况下,foo2, bar2.

我可以如上所述定义它们,附加后缀(1,2,3 ..)foo, bar, noo.然而,这并不漂亮,因为后缀可能很长; 它也不允许foo1bar1(vs. 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]
Run Code Online (Sandbox Code Playgroud)

编辑:我想这是相关的讨论来解释我是如何将接近它通过Java Interface(几个漂亮的,但概念层次,讨论Java interfaceHaskell typeclass可以在这里找到这篇文章,并在这里.)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) {...};
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*ani 6

一种好方法是将所有函数放入单个数据类型中.然后针对每种不同的策略使用该类型的不同值.最后,选择默认策略,并将实际功能链接到默认策略(为了便于使用).例如:

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 :)
  }
Run Code Online (Sandbox Code Playgroud)

这允许许多有用的功能:

  1. 可定制的策略(例如customAddStrategy).你也可以混合和匹配策略,例如newStrat = defaultStrategy { nooWithStrategy = nooWithStrategy tripleStrategy, fooWithStrategy = (*4) }
  2. 用户可以在运行时切换策略
  3. 默认值(即foo,barnoo)可供用户新图书馆
  4. 您或其他用户可以通过更多策略轻松扩展.

  • @Causality:这是正确的,当函数的类型需要改变时,你只需要类型类(例如`show`可以有`Int - > String`和`Bool - > String`类型).虽然您可以使用类型类为您的问题构建解决方案(例如,使用幻像类型来指定要使用的实例),但只使用内部函数的数据类型更简单,更好.有关使用数据类型比使用类型类更简单的另一个示例,请参阅[haskell antipattern:existential typeclass](http://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/). (2认同)