我有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()并不能帮助解决问题。)
答案 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.splitted和http://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类型。
可能值得考虑一种实用方法&#34; deepForced&#34; (?)使Java开发人员免受这些细节的影响。