0

In most of programming languages that support mutable variables, one can easily implement something like this Java example:

interface Accepter<T> {
    void accept(T t);
}

<T> T getFromDoubleAccepter(Accepter<Accepter<T>> acc){
    final List<T> l = new ArrayList<T>();
    acc.accept(new Accepter<T>(){

        @Override
        public void accept(T t) {
            l.add(t);
        }

    });
    return l.get(0); //Not being called? Exception!
}

Just for those do not understand Java, the above code receives something can can be provided a function that takes one parameter, and it supposed to grape this parameter as the final result.

This is not like callCC: there is no control flow alternation. Only the inner function's parameter is concerned.

I think the equivalent type signature in Haskell should be

getFromDoubleAccepter :: (forall b. (a -> b) -> b) -> a

So, if someone can gives you a function (a -> b) -> b for a type of your choice, he MUST already have an a in hand. So your job is to give them a "callback", and than keep whatever they sends you in mind, once they returned to you, return that value to your caller.

But I have no idea how to implement this. There are several possible solutions I can think of. Although I don't know how each of them would work, I can rate and order them by prospected difficulties:

  • Cont or ContT monad. This I consider to be easiest.

  • RWS monad or similar.

  • Any other monads. Pure monads like Maybe I consider harder.

  • Use only standard pure functional features like lazy evaluation, pattern-matching, the fixed point contaminator, etc. This I consider the hardest (or even impossible).

I would like to see answers using any of the above techniques (and prefer harder ways).

Note: There should not be any modification of the type signature, and the solution should do the same thing that the Java code does.

UPDATE

Once I seen somebody commented out getFromDoubleAccepter f = f id I realize that I have made something wrong. Basically I use forall just to make the game easier but it looks like this twist makes it too easy. Actually, the above type signature forces the caller to pass back whatever we gave them, so if we choose a as b then that implementation gives the same expected result, but it is just... not expected.

Actually what came up to my mind is a type signature like:

getFromDoubleAccepter :: ((a -> ()) -> ()) -> a

And this time it is harder.

Another comment writer asks for reasoning. Let's look at a similar function

getFunctionFromAccepter :: (((a -> b) -> b) -> b) -> a -> b

This one have an naive solution:

getFunctionFromAccepter f = \a -> f $ \x -> x a

But in the following test code it fails on the third:

exeMain = do
    print $ getFunctionFromAccepter (\f -> f (\x -> 10)) "Example 1" -- 10
    print $ getFunctionFromAccepter (\f -> 20) "Example 2" -- 20
    print $ getFunctionFromAccepter (\f -> 10 + f (\x -> 30)) "Example 3" --40, should be 30

In the failing case, we pass a function that returns 30, and we expect to get that function back. However the final result is in turn 40, so it fails. Are there any way to implement doing Just that thing I wanted?

If this can be done in Haskell there are a lot of interesting sequences. For example, tuples (or other "algebraic" types) can be defined as functions as well, since we can say something like type (a,b) = (a->b->())->() and implement fst and snd in term of this. And this, is the way I used in a couple of other languages that do not have native "tuple" support but features "closure".

4

3 回答 3

10

的类型acceptvoid accept(T)等效的 Haskell 类型t -> IO ()(因为 Java 中的每个函数本质上都是IO)。因此getFromDoubleAccepted可以直接翻译为

import Data.IORef

type Accepter t = t -> IO ()

getFromDoubleAccepter :: Accepter (Accepter a) -> IO a
getFromDoubleAccepter acc = do
    l <- newIORef $ error "Not called"
    acc $ writeIORef l
    readIORef l

如果您想要在 Haskell 中使用惯用的非 IO 解决方案,那么除了尝试模仿一些 Java 模式之外,您还需要更具体地了解您的实际最终目标是什么。

编辑:关于更新

getFromDoubleAccepter :: ((a -> ()) -> ()) -> a

很抱歉,这个签名绝不等同于 Java 版本。你的意思是,对于 any a,给定一个函数,该函数接受一个接受a但不返回任何东西或做任何副作用的函数,你想以某种方式想出一个 type 的值a。满足给定签名的唯一实现本质上是:

getFromDoubleAccepter :: ((a -> ()) -> ()) -> a
getFromDoubleAccepter f = getFromDoubleAccepter f
于 2013-08-13T06:12:44.720 回答
7

首先,我会尽可能地音译。我将把这些计算提升到一个单子,因为accept返回void(在 Haskell-land 中读取()),除非有一些影响,否则这是无用的。

type Accepter m t = t -> m ()

getFromDoubleAccepter :: (MonadSomething m) => Accepter m (Accepter m t) -> m t
getFromDoubleAccepter acc = do
    l <- {- new mutable list -}
    acc $ \t -> add l t
    return (head l)

当然,我们不能像那样制作一个可变列表,所以我们必须在这里使用一些直观的火花。当一个动作只是将一个元素添加到某个累加器时,我会想到Writermonad。所以也许那条线应该是:

    acc $ \t -> tell [t]

由于您只是在最后返回列表的头部,这没有任何影响,我认为签名应该变成:

getFromDoubleAccepter :: Accepter M (Accepter M t) -> t

哪里M是合适的单子。它需要能够写[t]s,这给了我们:

type M t = Writer [t]

getFromDoubleAccepter :: Accepter (M t) (Accepter (M t) t) -> t

现在这个函数的类型告诉我们如何编写它的其余部分:

getFromDoubleAccepter acc = 
    head . execWriter . acc $ \t -> tell [t]

我们可以检查它是否做了什么...

ghci> getFromDoubleAccepter $ \acc -> acc 42
42

所以这似乎是对的,我猜。我仍然有点不清楚这段代码应该是什么意思。

类型签名中的显式M t对我来说在美学上有点麻烦。如果我知道我正在解决什么问题,我会仔细研究。如果您的意思是参数可以是命令序列,但没有可用的计算功能,那么您可以将类型签名专门用于:

getFromDoubleAccepter :: (forall m. (Monad m) => Accepter m (Accepter m t)) -> t

这仍然适用于我们的示例。当然,这有点傻。考虑

   forall m. (Monad m) => Accepter m (Accepter m t))
=  forall m. (Monad m) => (t -> m ()) -> m ()

这种类型的函数唯一能做的就是用各种ts 依次调用它的参数,然后返回()。这样一个函数中的信息完全由那些ts 来表征[1],所以我们可以很容易地使用

getFromDoubleAccepter :: [t] -> t
getFromDoubleAccepter = head

[1] 只要我什么都不做,我还不如说在无穷大面前这不太准确。计算

crazy :: Integer -> Accepter m (Accepter m Integer)
crazy n acc = crazy (n+1) >> acc n

可用于形成无限序列

... >> acc 3 >> acc 2 >> acc 1 >> acc 0

它没有第一个元素。如果我们试图将其解释为列表,则在尝试查找第一个元素时会出现无限循环。然而,这种计算比无限循环有更多的信息——如果我们使用Last幺半群而不是列表来解释它,我们将能够0从结尾提取。所以真的

forall m. (Monad m) => Accepter m (Accepter m t)

与比列表稍微更一般的东西同构;特别是一个自由的幺半群。

于 2013-08-13T04:07:09.853 回答
0

感谢上面的答案,我终于得出结论,在 Haskell 中我们可以做一些与其他语言不同的事情。

实际上,这篇文章的动机是翻译著名的“单公理经典逻辑归约系统”。我已经用其他一些语言实现了这一点。执行应该没问题

Axiom: (a|(b|c)) | ((d|(d|d)) | ((e|b) | ((a|e) | (a|e))))

然而,由于减少规则看起来像

Rule: a|(b|c), a |-- c

需要提取内部参数作为最终结果。在其他语言中,这是通过使用诸如可变槽之类的副作用来完成的。然而,在 Haskell 中,我们没有可变槽并且涉及IO会很丑,所以我一直在寻找解决方案。

乍一看(如我的问题所示),这getFromDoubleAccepter f = f id似乎是胡说八道,但我意识到它在这种情况下确实有效!例如:

rule :: (forall r.a -> (b -> c -> r) -> r) -> a -> c
rule abc a = abc a $ flip const

技巧还是一样的:由于存在限定r对调用者隐藏,并且由被调用者为其选择类型,我们可以指定c为 be r,因此我们只需应用给定的函数即可获得结果。另一方面,给定的函数必须使用我们的输入来产生最终答案,因此它有效地将实现限制为我们真正想要的!

把它们放在一起,让我们看看我们能用它做什么:

newtype I r a b = I { runI :: a -> b -> r }

rule :: (forall r. I r a (I r b c)) -> a -> c
rule (I abc) a = abc a (I (\b c -> c))

axiom :: I r0 (I r1 a (I r2 b c)) 
          (I r0 (I r3 d (I r3 d d)) 
                (I r4 (I r2 e b) (I r4 (I r1 a e) (I r1 a e))))
axiom = let 
        a1 (I eb) e = I $ \b c -> eb e b
        a2 =  I $ \d (I dd) -> dd d d
        a3 (I abc) eb = I $ \a e -> abc a (a1 eb e)
        a4 abc = I $ \eb aeae -> runI a2 (a3 abc eb) aeae
    in I $ \abc (I dddebaeae) -> dddebaeae a2 (a4 abc)

在这里,我使用命名约定来跟踪类型签名:变量名称由“有效”类型变量组合(意味着它不是结果类型 - 所有r*类型变量)。

我不会重复选址文章中所代表的证明,但我想展示一些东西。在上面的定义中,axiom我们使用了一些 let 绑定变量来构造结果。毫不奇怪,这些变量本身可以通过使用rule和提取axiom。让我们看看如何:

--Equal to a4
t4 :: I r0 a (I r1 b c) -> I r2 (I r1 d b) (I r2 (I r0 a d) (I r0 a d))
t4 abc = rule axiom abc

--Equal to a3
t3 :: I r0 a (I r1 b c) -> I r1 d b -> I r0 a d
t3 abc eb = rule (t4 abc) eb

--Equal to a2
t2 :: I r a (I r a a)
t2 = rule (t3 axiom (t3 (t4 axiom) axiom)) axiom

--Equal to a1
t1 :: I r a b -> a -> I r b c
t1 ab a = rule (t3 t2 (t3 (t3 t2 t2) ab)) a

剩下要证明的一件事是,我们只能用t1tot4来证明所有重言式。我觉得是这样,但还没有证明。

与其他语言相比,Haskell 称呼似乎更有效且更具表现力。

于 2013-08-21T11:37:26.397 回答