根据类型为泛型函数提供不同的函数体

发布于 12-20 14:01 字数 533 浏览 5 评论 0原文

假设我有一些泛型函数,

genericFunc :: a -> b
genericFunc x = doSomeHardWork

但对于特定类型,有一种更有效的方法可以完成genericFunc。

genericFunc :: ParticularType -> b
genericFunc x = doSomeEasyWork

将这两个函数体组合到同一个 genericFunc 中的最佳方法是什么,这样当在 PspecialType 上使用时,它将 doSomeEasyWork,但是当用在其他类型上,它会doSomeHardWork吗?我特别排除使用不同名称或不同模块的选项。

我相信这可以通过类型类来完成,但我对使用语言编译指示的解决方案更感兴趣。我有一种模糊的感觉,这可以通过语言实用来完成,但我不知道如何做。如果您比较和对比这些方法和/或任何其他可能的方法,则会获得加分。

Suppose I have some generic function

genericFunc :: a -> b
genericFunc x = doSomeHardWork

But for a particular type, there is a much more efficient way that genericFunc could be done.

genericFunc :: ParticularType -> b
genericFunc x = doSomeEasyWork

What is the best way to combine these two function bodies into the same genericFunc, such that when used on ParticularType, it will doSomeEasyWork, but when used on other types, it will doSomeHardWork? I'm specifically excluding the option of using a different name, or different modules.

I believe this can be done with a typeclass, but I am more interested in solutions that use language pragmas. I have a vague inkling that this can be done with language pragmas but I have no idea how. Bonus points if you compare and contrast these approaches, and/or any other possible approaches.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

三岁铭2024-12-27 14:01:54

这可以通过在类定义中定义通用方法并在实例中覆盖它来使用类型类来完成。被覆盖的函数将始终被使用。

class ContainsInt c where
  toList :: c -> [Int]

  -- generic function
  elem :: Int -> c -> Bool
  elem n x = Prelude.elem n (toList x)

instance ContainsInt () where
  toList _ = []

  -- Override the generic function for type ()
  elem _ _ = False

GHC 支持的另一种方法是使用重写规则。重写规则告诉 GHC 只要有可能就用另一个表达式替换一个表达式。如果替换的类型错误,则不会完成,因此您可以使用它来用专门的版本替换函数。重写规则由 {-# RULES #-} 编译指示给出。

class ContainsInt c where
  toList :: c -> [Int]

elem :: ContainsInt c => Int -> c -> Bool
elem n x = Prelude.elem n (toList x)

-- Replace 'elem' by 'elemUnit' if it has the same type
{-# RULES "elem()" forall. elem = elemUnit #-}

elemUnit :: Int -> () -> Bool
elemUnit _ _ = False

重写规则由编译器自行决定执行,因此在任何给定情况下可能会或可能不会调用专用函数。例如,重写可能取决于编译器是否决定内联函数:

foo :: ContainsInt c -> Int -> [c] -> [Bool]
-- Must use the generic function
foo n cs = map (elem n) cs

useFoo :: Int -> [()] -> [Bool]
-- If 'foo' is inlined and 'elem' is not inlined, then this function will contain a rewritable call to 'elem'.
-- Otherwise rewriting cannot happen.
useFoo n cs = foo n cs

This can be done with type classes by defining the general-purpose method in the class definition and overriding it in an instance. The overridden function will always be used.

class ContainsInt c where
  toList :: c -> [Int]

  -- generic function
  elem :: Int -> c -> Bool
  elem n x = Prelude.elem n (toList x)

instance ContainsInt () where
  toList _ = []

  -- Override the generic function for type ()
  elem _ _ = False

An alternative supported by GHC is to use a rewrite rule. The rewrite rule tells GHC to replace one expression by another whenever possible. If the replacement is ill-typed, it will not be done, so you can use this to replace a function by a specialized version. The rewrite rule is given by a {-# RULES #-} pragma.

class ContainsInt c where
  toList :: c -> [Int]

elem :: ContainsInt c => Int -> c -> Bool
elem n x = Prelude.elem n (toList x)

-- Replace 'elem' by 'elemUnit' if it has the same type
{-# RULES "elem()" forall. elem = elemUnit #-}

elemUnit :: Int -> () -> Bool
elemUnit _ _ = False

Rewrite rules are performed at the discretion of the compiler, so the specialized function may or may not be called in any given situation. For example, the rewrite may depend on whether the compiler decides to inline a function:

foo :: ContainsInt c -> Int -> [c] -> [Bool]
-- Must use the generic function
foo n cs = map (elem n) cs

useFoo :: Int -> [()] -> [Bool]
-- If 'foo' is inlined and 'elem' is not inlined, then this function will contain a rewritable call to 'elem'.
-- Otherwise rewriting cannot happen.
useFoo n cs = foo n cs
缪败2024-12-27 14:01:54

对于 GHC,您可以使用 规则编译指示

{-# RULES "genericFunc/easy" genericFunc = doSomeEasyWork #-}

只要类型匹配,这就会应用重写规则,否则使用通用实现。

With GHC, you can use a RULES pragma.

{-# RULES "genericFunc/easy" genericFunc = doSomeEasyWork #-}

This will apply the rewrite rule whenever the types match and use the generic implementation otherwise.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文