2

我的弗雷格代码如下(主要是注意getDatabase的类型签名)

module fregeHelper.FregeCode where 

--Java's String.split method
pure native split :: String -> String -> JArray String

--Java's ArrayList<t>
data ArrayList t =native java.util.ArrayList where
    native new :: () -> STMutable s (ArrayList t)
    native add::Mutable s (ArrayList t)-> t -> ST s ()

getDatabase::String->(IO (STMutable s (ArrayList (String, String))))
getDatabase s = do
        fileContents <- readFile s
        let processedData = map ((\x->(elemAt x 0, elemAt x 1)) . (flip split ";")) . lines $ fileContents
        return $ fold foldAdd (ArrayList.new ()) processedData
    where
        foldAdd::ST s (Mutable s (ArrayList t)) -> t -> ST s (Mutable s (ArrayList t))
        foldAdd list elem = list >>= \x->(ArrayList.add x elem >> return x)

然后从Java我想定义以下函数(其中DATABASE是一个字符串常量):

private void readDatabase() {
    myList = Delayed.<ArrayList<TTuple2>>forced(
            fregeHelper.FregeCode.getDatabase(DATABASE));
}

然而,这给了我一个java.lang.ClassCastException: frege.prelude.PreludeBase$TST$1 cannot be cast to java.util.ArrayList

通过实验,我不得不将代码更改为

private void readDatabase() {
    fighters = Delayed.<ArrayList<TTuple2>>forced(
            fregeHelper.FregeCode.getDatabase(DATABASE)
            .apply(null)
            .apply(null)
            );
}

我在后者中添加了 null 只是为了表明我传入的内容并不重要。我不知道为什么我必须应用该函数三次(我不能立即强制评估)。有什么办法可以删除应用程序或对为什么它们是必要的有一些合理化?(注意:使用 .result() 无济于事。)

4

2 回答 2

3

这样做的原因是,在这个实现中,ST 动作被表示为一个“函数对象”,其中实现伴随函数的方法忽略它的参数。

回忆ST的定义可能有助于理解:

abstract data ST s a = ST (s -> a) where ...

首先要注意的是,它data实际上是用newtypeHaskell 编写的。所以ST只是一个类型重命名,也就是说,一个 ST 动作实际上是一个函数。

但是,这abstract确保您无法查看ST数据构造函数,因此无法直接从 Frege 代码运行该函数。

这解释了为什么在 Java 中,在将参数应用于返回ST动作的函数之后,需要将额外的参数应用于结果,正如我们所见,结果只不过是另一个函数。

那么,为什么你必须在你的代码中这样做两次呢?因为IO是(从我的头顶):

type IO = ST RealWorld

并且STMutable

type STMutable s x = ST s (Mutable s x)

所以问题出在你的getDatabase函数上,它返回一个IO动作,执行时返回一个 ST 动作,执行时返回一个可变 ArrayList。

这可能不是你想要的。而且我猜您在 中的最后一行挣扎了一段时间getDatabase,这可能应该是:

list <- ArrayList.new ()
foldM (\xs\x -> ArrayList.add xs x >> return xs) list processedData 

返回类型是然后

IO (Mutable RealWorld ArraList)

要不就

IOMutable ArrayList

另外一点:你不需要重新引入split,它已经存在了。您可以编写将分号分隔的输入行分开的行:

[ (a,b) | line <- lines fileContent, [a,b] <- ´;´.splitted line ]

另见http://www.frege-lang.org/doc/frege/java/util/Regex.html#Regex.splittedhttp://www.frege-lang.org/doc/frege/java/util/ Regex.html#Regex.split

添加

Dierks 的回答提出了一个有趣的观点,即我们应该有一个实用函数来从 Java 代码运行 ST(或 IO)操作。事实上,有这样一个函数,它的名字是ST.performUnsafe(在 Frege 中),在 Haskell 中被称为unsafePerformIO.

事实上,使用此函数将使 Java 代码更健壮地抵抗实现中的更改,因此强烈建议代替.apply(null)此处使用的代码。

于 2015-09-27T12:37:21.470 回答
1

我相信其他人可以提供更好的答案,但是在强制评估延迟时必须传递的看起来像额外参数的原因是 IO 和 STMutable 类型。

我在这里遇到了同样的问题:https ://github.com/Frege/FregeFX/blob/f2f548071afd32a08e9b24f6fb6bbece74d4213b/fregefx/src/main/java/org/frege/FregeFX.java#L19-L19

可能值得考虑一种实用方法“deeplyForced”(?),它可以保护 Java 开发人员免受这些细节的影响。

于 2015-09-27T12:23:59.583 回答