从 Java 调用 Frege 与参数数量不匹配
Calling Frege From Java Doesn't Match Number of Parameters
我有如下弗雷格代码(主要是注意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 cannot be cast to java.util.ArrayList
通过实验,我不得不将代码更改为
private void readDatabase() {
fighters = Delayed.<ArrayList<TTuple2>>forced(
fregeHelper.FregeCode.getDatabase(DATABASE)
.apply(null)
.apply(null)
);
}
我把 null 放在后者 apply 中只是为了表明我传入的内容无关紧要。我不知道为什么我必须应用该函数三次(我不能立即强制求值).有什么办法可以删除应用程序或对为什么需要它们进行一些合理化?
(注意:使用 .result() 对这种情况没有帮助。)
我相信其他人可以提供更好的答案,但是在强制评估延迟时必须传递额外参数的原因是 IO 和 STMutable 类型。
可能值得考虑一种实用方法 "deeplyForced" (?) 来保护 Java 开发人员免受这些细节的影响。
这样做的原因是,在此实现中,ST 操作表示为 "function object",其中实现伴随函数的方法忽略它的参数。
回忆一下ST的定义可能有助于理解:
abstract data ST s a = ST (s -> a) where ...
首先要注意的是 data
实际上会写成 Haskell 中的 newtype
。所以ST
只是类型重命名,即一个ST动作其实是一个函数
但是,abstract
确保您无法查看 ST
数据构造函数,因此不能 运行 直接从 Frege 代码中调用函数。
这解释了为什么从 Java 开始,在将参数应用到 return 是一个 ST
操作的函数之后,需要将该额外参数应用到结果,即,正如我们所见,只不过是另一个功能。
那么,为什么要在代码中执行两次呢?因为 IO
是(从我的头顶):
type IO = ST RealWorld
并且STMutable
是
type STMutable s x = ST s (Mutable s x)
所以问题出在你的getDatabase
函数上,它return是一个IO
动作,当执行时,return是一个ST动作,它,执行时,return 是一个可变的 ArrayList。
这可能不是您想要的。我猜你在 getDatabase
中的最后一行挣扎了一段时间,它可能应该是:
list <- ArrayList.new ()
foldM (\xs\x -> ArrayList.add xs x >> return xs) list processedData
当时的return类型
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 and http://www.frege-lang.org/doc/frege/java/util/Regex.html#Regex.split
加法
Dierks 的回答提出了一个有趣的观点,即我们应该为 Java 代码中的 运行ning ST(或 IO)操作提供一个效用函数。事实上,有这样一个函数,它的名字是 ST.performUnsafe
(在 Frege 中),在 Haskell 中被称为 unsafePerformIO
。
事实上,使用此函数会使 Java 代码对实现中的更改更加健壮,因此强烈建议代替此处使用的 .apply(null)
代码。
我有如下弗雷格代码(主要是注意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 cannot be cast to java.util.ArrayList
通过实验,我不得不将代码更改为
private void readDatabase() {
fighters = Delayed.<ArrayList<TTuple2>>forced(
fregeHelper.FregeCode.getDatabase(DATABASE)
.apply(null)
.apply(null)
);
}
我把 null 放在后者 apply 中只是为了表明我传入的内容无关紧要。我不知道为什么我必须应用该函数三次(我不能立即强制求值).有什么办法可以删除应用程序或对为什么需要它们进行一些合理化? (注意:使用 .result() 对这种情况没有帮助。)
我相信其他人可以提供更好的答案,但是在强制评估延迟时必须传递额外参数的原因是 IO 和 STMutable 类型。
可能值得考虑一种实用方法 "deeplyForced" (?) 来保护 Java 开发人员免受这些细节的影响。
这样做的原因是,在此实现中,ST 操作表示为 "function object",其中实现伴随函数的方法忽略它的参数。
回忆一下ST的定义可能有助于理解:
abstract data ST s a = ST (s -> a) where ...
首先要注意的是 data
实际上会写成 Haskell 中的 newtype
。所以ST
只是类型重命名,即一个ST动作其实是一个函数
但是,abstract
确保您无法查看 ST
数据构造函数,因此不能 运行 直接从 Frege 代码中调用函数。
这解释了为什么从 Java 开始,在将参数应用到 return 是一个 ST
操作的函数之后,需要将该额外参数应用到结果,即,正如我们所见,只不过是另一个功能。
那么,为什么要在代码中执行两次呢?因为 IO
是(从我的头顶):
type IO = ST RealWorld
并且STMutable
是
type STMutable s x = ST s (Mutable s x)
所以问题出在你的getDatabase
函数上,它return是一个IO
动作,当执行时,return是一个ST动作,它,执行时,return 是一个可变的 ArrayList。
这可能不是您想要的。我猜你在 getDatabase
中的最后一行挣扎了一段时间,它可能应该是:
list <- ArrayList.new ()
foldM (\xs\x -> ArrayList.add xs x >> return xs) list processedData
当时的return类型
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 and http://www.frege-lang.org/doc/frege/java/util/Regex.html#Regex.split
加法
Dierks 的回答提出了一个有趣的观点,即我们应该为 Java 代码中的 运行ning ST(或 IO)操作提供一个效用函数。事实上,有这样一个函数,它的名字是 ST.performUnsafe
(在 Frege 中),在 Haskell 中被称为 unsafePerformIO
。
事实上,使用此函数会使 Java 代码对实现中的更改更加健壮,因此强烈建议代替此处使用的 .apply(null)
代码。