繁体   English   中英

Haskell设计最佳实践,涉及具有相似功能集的多个模块

[英]Haskell design best practice for multiple modules assocated with similar set of functions

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

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

一种替代方法是将每个方案都视为单独的Module 现在foo, bar, noo每种情况下的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 ,可以发现这个职位 ,并在这里 。) Java interface and class在很多情况下, 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 :)
  }

这提供了许多有用的功能:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM