如何使用Haskell限制函数参数?

时间:2017-12-12 09:34:24

标签: haskell

让我们假设四个函数f1,f2,f3,f4 :: SomeMonad m => m a -> m a,其中f1f2在概念上相关,同样f3f4

现在可以编写一个函数f :: SomeMonad m => (m a -> m a) -> m a,可以将任何f1..f4作为输入。然后可以编写f1 $ f f2

问题是,如何编写一个函数g,使其仅接受f1f2作为参数?我们应该能够写f4 $ g f1而ghc应该拒绝g f3

如果我尝试添加class,例如:: (SomeMonad m, Some_G_class a) =>,则返回类型a也会受到约束,然后无法应用f4。同样地,似乎m无法修改。

延迟添加

Daniel Wagner和leftroundabout的答案似乎都可以使用。

以下是关于实际(和实际)问题的更多信息。

  • 底层lib包含数十个返回m a
  • 的函数
  • 构建了lib,以便可以链接函数,例如与(.)一样f1 . f2 . f3
  • 从概念上讲,这些功能大约有五到十个子集,子集重叠。这些函数应该在每个子集中是可链接的,但不能与其他子集中的函数一起使用。

使用代理

似乎

data Type = A | B | C ... | H
data Proxy (ty :: Type) = Proxy

可用于对函数进行分类。要处理重叠使用,可以调用函数,如

h1 :: SomeMonad t m => Either (Proxy A) (Proxy B) -> m t -> m t
h1 (Left (Proxy :: Proxy A) mt = ..
h1 (Right (Proxy :: Proxy B) mt = ..

可以做到。但是如果属于A, B, CD的函数怎么办?也许有OneOfAny之类的内容可以在这里使用?

使用RankNTypes

似乎SpecificMonad - 约束(限制?)开始支持函数f3f4的使用网站(即全部)。我觉得如果我尝试管理5-10个重叠函数集,那么那些在每个地方传播的约束都会遇到严重的困难。是这种情况吗?

所以我们需要一个函数来强制执行,可能就像

fg :: (SomeMonad t m1, SpecificMonad t m2) => m2 t -> m1 t
fg a = pure $ fromSpecM a

和atm假设SomeMonadpure。在我的情况下,我无法将fromSpecM添加到SomeMonad的类方法中,但可能在SpecificMonad中没有问题。我在这方面的第一次试验仍然过于简单,以至于我可以看出这是否可行。很可能我在这里看不到一些非常明显的东西。

在某种程度上,SpecificMonad不应该有任何其他方法,它应该只是作为"本地env / fix / hack"不影响代码的其他部分。那么,还有其他方法可以实现像fg

这样的事情

2 个答案:

答案 0 :(得分:3)

如果f1f2f3f4都具有相同的类型签名,则类型系统无法区分它们(duh),所以你也不能限制哪些应该有效。

因此,在您可以实现目标之前,您需要在签名中加以区分。可能最明智的方法是改进monad级约束:

class SomeMonad m => SpecificMonad m

f1, f2 :: SomeMonad m => m a -> m a     -- same as before
f3, f4 :: SpecificMonad m => m a -> m a -- more restrictive

(您不需要在此处更改实施,因为SpecificMonad保证SomeMonad。)

现在,f :: SomeMonad m => (m a -> m a) -> m a本身并不强制该参数满足任何特定约束 - 您传入的任何函数只会将其约束添加到SomeMonad带来的f m约束,即

f f1 :: SomeMonad m => m a
f f3 :: SpecificMonad m => m a -- because `SomeMonad` is superclass, we don't need
                               -- to mention it, but it's actually implicit constraint

都很好。

但是,您可以假设f只接受具有特定不太具体约束的函数。这需要Rank-2多态性

{-# LANGUAGE RankNTypes, UnicodeSyntax #-}

g :: SomeMonad m => (∀ μ . SomeMonad μ => μ a -> μ a) -> m a
g φ = f φ

现在,您可以编写g f1,因为f1是一个适用于任何SomeMonad的多态函数。但是你不能写g f3,因为f3在它可以操作的monad中太挑剔了,即使m应该与g f3一起使用的外SpecificMonad也是如此履行g约束f3将不允许其参数访问该信息。

当然,要在任何其他合法设置(例如f4)中使用f4 $ g f1SpecificMonad,您必须为{添加实例{1}}他们应该与任何monad一起工作。这些是像

这样的单行
instance SpecificMonad []
instance SpecificMonad Maybe

(前提是它们已经有SomeMonad个实例)。

无Unicode编写:

g :: SomeMonad m => (forall m' . SomeMonad m' => m' a -> m' a) -> m a
g = f

必要时,如果你想写f4 $ g f1

答案 1 :(得分:1)

也许你可以使用一个简单的幻像类型参数。例如:

import { NativeModules } from 'react-native';

在ghci中,我们可以看到NativeModules.TestClass.test('C# called successfully.'); {-# LANGUAGE DataKinds #-} {-# LANGUAGE KindSignatures #-} data Type = A | B data Id (ty :: Type) a = Id a f1, f2 :: Id A a -> Id A a f1 = id f2 = id f3, f4 :: Id B a -> Id B a f3 = id f4 = id 组合得很好,f1f2也是如此,但不是f3f4

f1