Elm - Check the Type of a value

2019-07-13 06:38发布

Does a function exists that checks the Type of a variable in an Elm? For example (repl):

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

The motivation for this is that when you are using Signal.map[n] the situation usually arises that not all of the arguments to the function to be applied are Signals - they then usually have to be 'promoted' to Signals using Signal.constant - if I could check the type of such arguments, I could create a function Signal.allSigMap[n] that would turn such arguments into Signals automatically.

So

Signal.map2 grandFatherClock clockSignalElement (Signal.constant cabinetElement)

becomes

Signal.allSigMap2 grandFatherClock clockSignalElement cabinetElement

Could be bad practice though. Please let me know.

4条回答
相关推荐>>
2楼-- · 2019-07-13 06:53

I'll respond first to your intention to use returnType as a way of promoting types as necessary to Signal. That would entail that returnType or some other function along the way actually return a type rather than a String since there's no other way to make the type checker happy.

Such a function does not exist and cannot exist within Elm as it stands today. What you're asking for is something that can inspect the type of a value compile time and then run a function on that type.

To see why this is radically different from anything that currently exists in Elm, let's assume such a function exists.

returnType : a -> ?

We're immediately confronted with the first question of what exactly is the type of returnType. Let's handwave this and say we have a type for all types called Type (which has its own set of logical problems we'll leave aside).

returnType : a -> Type

How do we actually use this function? Presumably it'll be able to go in the type signature since it's returning a type.

 x : returnType 5

Now that is a type signature completely different from everything else in Elm. There's a numeric literal (and a function)! All of a sudden you can start to write things like this.

y = 5

x : returnType y
x = 6

That is way beyond what Elm's type system can do. That sort of (exciting and powerful) type level and value level mixing is known as dependent typing and no mainstream fully dependently typed languages exist; the closest things to mainstream are probably Coq, Agda, Idris, and ATS which are all fairly obscure.

As for the question as literally stated of having a function returnType : a -> String that prints out a string representing the type of a value, that's also not possible in Elm, although for other reasons. Such a function (which is something whose application occurs at runtime) must be able to reconstruct type information about a runtime value, but runtime values of Elm are just Javascript values; they've been stripped of their Elm types. You'll have to either reconstruct the original Elm type from the Javascript value (not always possible as different types can end up as the same Javascript value) or have special compiler support.

In the case of the Elm REPL, the latter is chosen. The whole REPL is written in Haskell and takes of advantage of how Elm types are implemented in the Haskell runtime.

查看更多
趁早两清
3楼-- · 2019-07-13 07:00

This is not possible (and not desirable, I would argue) due to Type Erasure (https://en.wikipedia.org/wiki/Type_erasure). Because everything in elm must be well typed, the compiler can verify all the types line up at compile time. Once that is done, it can safely remove all type information from the compiled code that actually gets run. This actually makes the code more efficient, as at runtime no type information (or runtime type checks, like you may be familiar with in typical javascript code) needs to be carried around with all your values.

I believe it's unlikely runtime type introspection would ever be added to languages like elm. Needing to check a type at runtime is a codesmell for poorly designed code.

查看更多
我想做一个坏孩纸
4楼-- · 2019-07-13 07:06

I also wondered why no instanceOf or typeOf in Elm. Then going through docs thoroughly for deeper understanding of FRP it became clear! You should know the shape of your data at all times. In order to do so using case...of pattern with Elms Tagged Unions you should be able to access your desired values by filtering your known types. Caveat, all branches of case...of must be of the same type, but that doesn't preclude you from using a tuple with superset of of all your known types with dummy sentinel values to allow you access your desired type.

See code below for illustration:

Assumptions:

1.Your desired types are tagged when used

2.Using sentinel values

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)
查看更多
迷人小祖宗
5楼-- · 2019-07-13 07:12

There's no such function in the core libraries. You'd need to write your own, which is half-done for you because you can reverse engineer elm-repl.

查看更多
登录 后发表回答