4

我正在尝试使用端口将 URL 传递给 Javascript,以便将用户重定向到另一个页面。我写了一个port module包含我的项目所需的所有端口:

port module Utils exposing (..)
port changePage : String -> Cmd Event

然后,我将它导入到我的 Elm 文件中:

type Event = PageChange String
import Utils exposing (changePage)

但是编译器不喜欢它:

It looks like the keyword `import` is being used as a variable.
8| import Utils exposing (changePage)
         ^
Rename it to something else.

所以我将端口定义移动到主文件:

type Event = PageChange String
port changePage : String -> Cmd Event

但编译器仍然不同意:

Port `changePage` has an invalid type.
28| port changePage : String -> Cmd Event
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You are saying it should be:
 String -> Platform.Cmd.Cmd BmC.Index.Event
But you need to use the particular format described here:
<http://guide.elm-lang.org/interop/javascript.html#ports>

所以我去看了那个特殊的格式,port check : String -> Cmd msg. 我不明白这是msg从哪里来的,所以我去检查了代码,我仍然不明白那行是什么意思。

从哪里来msg?是什么type Cmd msg = Cmd意思?提前致谢。

4

3 回答 3

10

import语句必须出现在任何函数或类型定义之前。这就是您在此代码上遇到编译错误的原因:

type Event = PageChange String
import Utils exposing (changePage)

关于端口的问题:Elm 的端口是架构边缘的一项功能。它们允许与不同的语言进行互操作,因此,无论出于何种意图和目的,幕后都有一点魔力。

其他语言具有与其他语言互操作的类似“神奇”构造。Haskell 具有外部函数接口 (FFI),而 C# 具有extern关键字来调用外部库。

的定义Cmd是那些通过查看代码并没有真正意义的部分之一。

type Cmd msg = Cmd

这并不能告诉我们太多,但没关系。它更像是编译器在编译时填充的占位符。其他语言也这样做。Haskell 经常使用底部调用let x = x in x来表示编译器将实际实现的函数。

因此,除非您对 Elm 和 Javascript 之间的交叉实现真正感兴趣,否则可以将其留给想象并接受魔法。

至于能够在您的端口中指定具体类型,那只是编译器团队的另一种选择。尽管 aCmd Event是唯一可以通过端口的东西,但他们选择强制泛型类型作为 Cmd 的类型参数。的确切拼写Cmd msg不是必需的。msg可以是任何小写字母:

port check : String -> Cmd a
于 2016-12-24T15:01:38.097 回答
5

Elm 中的端口声明设计需要非常具体的类型定义注释。

首先,我建议查看阅读类型,尤其是在提到类型变量的段落中

之后,确保您熟悉通用数据结构,如果官方指南没有帮助,您可以查看我对类似问题的回答Understanding generic union types in Elm

端口有点混乱,所以我打开了一个问题JavaScript 互操作指南应该解释端口函数类型定义

出港

port out : String    ->    Cmd msg
              |                 |
              |                 |
          Concrete type    Generic type
          of outgoing      with `msg` type variable
          data             
                           Outgoing port never sends messages,
                           but you need to specify the type so the
                           compiler can infer, that you can use
                           this command to send the outgoing values
                           in any module, that imports "out" function

进港

port in : (List String -> msg)    ->    Sub msg
                     |                     |
                     |                     |
         A function, that accepts       Has to be generic,
         outgoing data and returns      because it is going to be
         a message. This function       a subscription with the
         is called "tagger"             same type as "tagger" returns

         Has to be generic, i.e. use
         `msg` type variable
于 2016-12-24T14:48:45.697 回答
1

您的第一个编译器错误是因为您import的定义成功,但所需的格式是所有导入都在所有定义之前。

至于msgin是什么String -> Cmd msg意思,它只是一个类型变量——Elm 中类型注解中的所有小写类型名称都是类型变量,而以大写字母开头的都是具体类型。所以它与String -> Cmd aor相同String -> Cmd foo

在 Elm 中,就像在 Haskell 和其他一些函数式语言中一样,如果类型变量在类型注解中,它会被隐式地普遍量化。所以我们把上面的类型读为

changePage : ∀msg.String → Cmd msg

也就是说,'对于任何类型msgchangePage都可以采用 aString到 aCmdmsg'。

这意味着该表达式changePage "myPage.html"可以在您需要某种类型命令的代码中的任何地方使用。这似乎是有道理的,因为它代表了“请发送"myPage.html"给订阅changePage端口的任何人”的命令。

于 2016-12-24T16:22:30.130 回答