【问题标题】:Calling Frege From Java Doesn't Match Number of Parameters从 Java 调用 Frege 与参数数量不匹配
【发布时间】:2015-09-29 02:07:50
【问题描述】:

我的弗雷格代码如下(主要是注意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)
            );
}

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

【问题讨论】:

    标签: java compilation frege


    【解决方案1】:

    原因是在这个实现中,一个 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 
    

    返回类型是 then

    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) 代码。

    【讨论】:

    • 太好了,感谢您的回复。我不确定 IO/ST/Mutable/IOMutable/MutableIO 等的所有定义是如何嵌套和连接的。如果有这方面的资源,我将不胜感激。此外,ST.performUnsafe 在 frege 方面——是否有一种安全、可靠的方法来强制从 Java 方面执行?
    • @Crazycolorz5 好吧,所有这些都记录在frege-lang.org/doc/frege/prelude/PreludeIO.html 中 - 要了解如何将某些表达式转换为 Java,请在 REPL 中输入表达式并使用 :java 命令。例如, :load 并导入你的 Frege 模块,然后输入 ST.performUnsafe (getDatabase "foo") ,当类型检查器没问题时,输入 :java 与你的表达式对应的代码显示在末尾附近。
    【解决方案2】:

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

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

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

    【讨论】:

    • 这个实用方法确实存在,它的名字是IO.performUnsafe(我应该把这个添加到我的答案中)。但这里的重点是 OP 嵌套了两层 ST(我猜是无意的)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2012-12-06
    • 1970-01-01
    • 2022-06-28
    • 1970-01-01
    相关资源
    最近更新 更多