从Java调用Frege并不匹配参数数量

时间:2015-09-27 10:20:24

标签: java compilation frege

我有Frege代码如下(主要是注意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()并不能帮助解决问题。)

2 个答案:

答案 0 :(得分:3)

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

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

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

首先要注意的是data实际上会在Haskell中写成newtype。所以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)代码。

答案 1 :(得分:1)

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

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

可能值得考虑一种实用方法&#34; deepForced&#34; (?)使Java开发人员免受这些细节的影响。