Elm - 检查值的类型

Elm - Check the Type of a value

是否存在检查 Elm 中变量类型的函数?例如 (repl):

numberTwo = 2
.....
returnType numberTwo
"number" : String

这样做的动机是,当您使用 Signal.map[n] 时,通常会出现并非要应用的函数的所有参数都是信号的情况 - 它们通常必须是 'promoted'使用 Signal.constant 到信号 - 如果我可以检查此类参数的类型,我可以创建一个函数 Signal.allSigMap[n] 来自动将此类参数转换为信号。

所以

Signal.map2 grandFatherClock clockSignalElement (Signal.constant cabinetElement)

变成

Signal.allSigMap2 grandFatherClock clockSignalElement cabinetElement

虽然这可能是不好的做法。请告诉我。

核心库中没有这样的函数。您需要自己编写,这对您来说已经完成了一半,因为您可以对 elm-repl 进行逆向工程。

我会首先回应您打算使用 returnType 作为必要的类型提升 Signal 的方式。这将需要 returnType 或沿途的某些其他功能实际上 return 类型而不是 String 因为没有其他方法可以使类型检查器满意。

像今天这样的功能在 Elm 中不存在,也不可能存在。您所要求的是可以检查值编译时间的类型,然后 运行 该类型的函数。

要了解为什么这与 Elm 中当前存在的任何功能完全不同,让我们假设存在这样的功能。

returnType : a -> ?

我们立即面临第一个问题,即 returnType 的类型到底是什么。假设我们有一个适用于所有类型的类型,称为 Type(它有自己的一组逻辑问题,我们将放在一边)。

returnType : a -> Type

我们如何实际使用这个功能?据推测它将能够进入类型签名,因为它是 returning 类型。

 x : returnType 5

现在 是一个完全不同于 Elm 中其他所有内容的类型签名。有一个数字文字(和一个函数)!突然间你可以开始写这样的东西了。

y = 5

x : returnType y
x = 6

这远远超出了 Elm 的类型系统所能做的。这种(令人兴奋和强大的)类型级别和值级别的混合被称为 dependent typing 并且不存在主流的完全依赖类型的语言;最接近主流的可能是 Coq、Agda、Idris 和 ATS,它们都相当晦涩难懂。

至于字面意思是有一个函数 returnType : a -> String 打印出表示值类型的字符串的问题,尽管出于其他原因,这在 Elm 中也是不可能的。这样的函数(它的应用程序发生在 运行 时间)必须能够重建关于 运行 时间值的类型信息,但是 Elm 的 运行 时间值只是 Javascript 值;他们被剥夺了 Elm 类型。您必须从 Javascript 值重建原始 Elm 类型(并非总是可行,因为不同的类型最终可能具有相同的 Javascript 值)或具有特殊的编译器支持。

如果是Elm REPL,则选择后者。整个 REPL 是用 Haskell 编写的,并利用了 Elm 类型在 Haskell 运行 时间中的实现方式。

我也想知道为什么没有instanceOf或者typeOf 在榆树中。然后彻底阅读文档以更深入地了解 FRP,它变得清晰了!您应该始终了解数据的形状。为了使用 case...of pattern 和 Elms Tagged Union 来做到这一点,您应该能够通过过滤已知类型来访问所需的值。警告, case...of 的所有分支必须属于同一类型,但这并不妨碍您使用具有所有已知类型的超集的元组和虚拟标记值来允许您访问所需的类型。

参见下面的代码进行说明:

假设:

1.Your 需要的类型在使用时被标记

2.Using 标记值

import Html exposing (text)



    type Input = Nothing | ILetter String | INumber Int | IFloat Float

inputs: List (Input)
inputs = [ Nothing, IFloat 8.34, ILetter "A", INumber 5, INumber -1, IFloat -12.0, ILetter "123!"]


--
doSomething: Input -> (String, Int, Float)
doSomething myInput =
   case myInput of
    Nothing ->
       ("not here!", -69, 69.0)

    ILetter string ->
            if string /= "DarnString" then
             (string, 0, 0) 
            else
             ("DarnString", -1, -1.0)

    INumber int ->
            if int > 0 then
             ("GoodInt" , int, toFloat int)      
            else
             ("DarnInt" , int, toFloat int)

    IFloat float ->
            if float < 0 then 
             ("Goodfloat", truncate float, float)  
            else
             ("Darnfloat", truncate float, float)




-- I am only interested in using strings
myStringFilter (mString, mInt, mFloat) =
            if mString == "DarnString" || mString == "Darnfloat" ||  mFloat < 0  || mString == "DarnInt" || mInt > 0 then
              "We are not the String(s) you were looking for!"
            else
               mString

myFloatFilter (mString, mInt, mFloat) =
            if  mString == "DarnString" || mString == "Darnfloat" || mString == "DarnInt" then
              696969696.696969696969
            else                 
                mFloat

myIntFilter (mString, mInt, mFloat) =
            if  mString == "DarnString" || mString == "Darnfloat" || mString == "DarnInt" then
              -696969696
            else                 
                mInt               

main =
     text (toString <| List.map myStringFilter (List.map doSomething inputs)) 
    --text <| myStringFilter <| doSomething (IFloat 14.83)
    -- text  <| toString <| myFloatFilter <| doSomething (IFloat -14.83)
    --text  <| toString <| myIntFilter <| doSomething (INumber 5)

由于类型擦除 (https://en.wikipedia.org/wiki/Type_erasure),这是不可能的(我认为也是不可取的)。因为 elm 中的所有内容都必须类型正确,所以编译器可以在编译时验证所有类型是否一致。完成后,它可以安全地从实际获得 运行 的编译代码中删除所有类型信息。这实际上使代码更高效,因为在 运行 时不需要携带类型信息(或 运行 时类型检查,就像您在典型的 javascript 代码中可能熟悉的那样)带着你所有的价值观。

我认为 运行time 类型内省不太可能被添加到像 elm 这样的语言中。需要在 运行 时间检查类型是代码设计不佳的代码。