New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Runtime exception from gampleman/elm-visualization #806

Closed
jackalcooper opened this Issue Jan 14, 2017 · 9 comments

Comments

Projects
None yet
5 participants
@jackalcooper

jackalcooper commented Jan 14, 2017

image
image

I am not sure what this has anything to do with particular library. It happened when I'm using gampleman/elm-visualization pkg. It seems to have something to do with Date converted from Unix timestamp or overwhelming amount of DOM element(I'm using it to render a line chart with data sent through Phoenix Channel).

@process-bot

This comment has been minimized.

Show comment
Hide comment
@process-bot

process-bot Jan 14, 2017

Thanks for the issue! Make sure it satisfies this checklist. My human colleagues will appreciate it!

Here is what to expect next, and if anyone wants to comment, keep these things in mind.

process-bot commented Jan 14, 2017

Thanks for the issue! Make sure it satisfies this checklist. My human colleagues will appreciate it!

Here is what to expect next, and if anyone wants to comment, keep these things in mind.

@rtfeldman rtfeldman changed the title from Runtime exception to Runtime exception from gampleman/elm-visualization Jan 15, 2017

@rtfeldman

This comment has been minimized.

Show comment
Hide comment
@rtfeldman

rtfeldman Jan 15, 2017

Member

@gampleman - any ideas on how to narrow this down?

Member

rtfeldman commented Jan 15, 2017

@gampleman - any ideas on how to narrow this down?

@jackalcooper

This comment has been minimized.

Show comment
Hide comment
@jackalcooper

jackalcooper Jan 16, 2017

This should reproduce the error:

module LineChart exposing (..)

import Visualization.Scale as Scale exposing (ContinuousScale, ContinuousTimeScale)
import Visualization.Axis as Axis
import Visualization.List as List
import Visualization.Shape as Shape
import Date
import Date.Extra exposing (..)
import Svg exposing (..)
import Svg.Attributes exposing (..)
import Date exposing (Date)
import String


w : Float
w =
    900


h : Float
h =
    450


padding : Float
padding =
    30


view : List ( Date, Float ) -> Svg msg
view model =
    let
        _ =
            Debug.log "data breaks line chart" model

        xScale : ContinuousTimeScale
        xScale =
            let
                start =
                    Date.fromTime 1448928000000

                end =
                    Date.fromTime 1456790400000

                ( start_, _ ) =
                    Maybe.withDefault ( start, 0 ) (List.head model)

                ( end_, _ ) =
                    Maybe.withDefault ( start, 0 ) (List.reverse model |> List.head)
            in
                Scale.time ( add Day -1 start_, add Day 1 end_ ) ( 0, w - 2 * padding )

        yScale : ContinuousScale
        yScale =
            Scale.linear ( 0, 5 ) ( h - 2 * padding, 0 )

        opts : Axis.Options a
        opts =
            Axis.defaultOptions

        xAxis : Svg msg
        xAxis =
            Axis.axis { opts | orientation = Axis.Bottom, tickCount = List.length model } xScale

        yAxis : Svg msg
        yAxis =
            Axis.axis { opts | orientation = Axis.Left, tickCount = 5 } yScale

        areaGenerator : ( Date, Float ) -> Maybe ( ( Float, Float ), ( Float, Float ) )
        areaGenerator ( x, y ) =
            Just ( ( Scale.convert xScale x, Tuple.first (Scale.rangeExtent yScale) ), ( Scale.convert xScale x, Scale.convert yScale y ) )

        lineGenerator : ( Date, Float ) -> Maybe ( Float, Float )
        lineGenerator ( x, y ) =
            Just ( Scale.convert xScale x, Scale.convert yScale y )

        area : String
        area =
            List.map areaGenerator model
                |> Shape.area Shape.monotoneInXCurve

        line : String
        line =
            List.map lineGenerator model
                |> Shape.line Shape.monotoneInXCurve
    in
        svg [ width (toString w ++ "px"), height (toString h ++ "px") ]
            [ g [ transform ("translate(" ++ toString (padding - 1) ++ ", " ++ toString (h - padding) ++ ")") ]
                [ xAxis ]
            , g [ transform ("translate(" ++ toString (padding - 1) ++ ", " ++ toString padding ++ ")") ]
                [ yAxis ]
            , g [ transform ("translate(" ++ toString padding ++ ", " ++ toString padding ++ ")"), class "series" ]
                [ Svg.path [ d area, stroke "none", strokeWidth "3px", fill "rgba(255, 0, 0, 0.54)" ] []
                , Svg.path [ d line, stroke "red", strokeWidth "3px", fill "none" ] []
                ]
            ]



-- From here onwards this is simply example boilerplate.
-- In a real app you would load the data from a server and parse it, perhaps in
-- a separate module.


main =
    view model



-- Here we simply define the data inline. The examples don't include logic for fetching and parsing this data.


model =
    [ ( Date.fromTime 1483521850, 0.107 )
    , ( Date.fromTime 1483521850, 0.266 )
    , ( Date.fromTime 1483521850, 0.285 )
    , ( Date.fromTime 1483521827, 0.084 )
    , ( Date.fromTime 1483521751, 0.081 )
    , ( Date.fromTime 1483521649, 0.073 )
    , ( Date.fromTime 1483521635, 0.049 )
    , ( Date.fromTime 1483521699, 0.359 )
    , ( Date.fromTime 1483521698, 0.089 )
    , ( Date.fromTime 1483521688, 0.146 )
    , ( Date.fromTime 1483521658, 0.112 )
    , ( Date.fromTime 1483521657, 0.055 )
    , ( Date.fromTime 1483521858, 0.069 )
    , ( Date.fromTime 1483521842, 0.049 )
    , ( Date.fromTime 1483521750, 0.126 )
    , ( Date.fromTime 1483521747, 0.1 )
    , ( Date.fromTime 1483521746, 0.132 )
    , ( Date.fromTime 1483521650, 0.063 )
    , ( Date.fromTime 1483521646, 0.059 )
    , ( Date.fromTime 1483521659, 0.078 )
    , ( Date.fromTime 1483521881, 0.054 )
    , ( Date.fromTime 1483521820, 0.057 )
    , ( Date.fromTime 1483521729, 0.167 )
    , ( Date.fromTime 1483521753, 0.063 )
    , ( Date.fromTime 1483521661, 0.057 )
    , ( Date.fromTime 1483521863, 0.061 )
    , ( Date.fromTime 1483521750, 0.07 )
    , ( Date.fromTime 1483521745, 0.126 )
    , ( Date.fromTime 1483521722, 0.126 )
    , ( Date.fromTime 1483521642, 0.436 )
    , ( Date.fromTime 1483521628, 0.068 )
    , ( Date.fromTime 1483521679, 0.067 )
    ]

jackalcooper commented Jan 16, 2017

This should reproduce the error:

module LineChart exposing (..)

import Visualization.Scale as Scale exposing (ContinuousScale, ContinuousTimeScale)
import Visualization.Axis as Axis
import Visualization.List as List
import Visualization.Shape as Shape
import Date
import Date.Extra exposing (..)
import Svg exposing (..)
import Svg.Attributes exposing (..)
import Date exposing (Date)
import String


w : Float
w =
    900


h : Float
h =
    450


padding : Float
padding =
    30


view : List ( Date, Float ) -> Svg msg
view model =
    let
        _ =
            Debug.log "data breaks line chart" model

        xScale : ContinuousTimeScale
        xScale =
            let
                start =
                    Date.fromTime 1448928000000

                end =
                    Date.fromTime 1456790400000

                ( start_, _ ) =
                    Maybe.withDefault ( start, 0 ) (List.head model)

                ( end_, _ ) =
                    Maybe.withDefault ( start, 0 ) (List.reverse model |> List.head)
            in
                Scale.time ( add Day -1 start_, add Day 1 end_ ) ( 0, w - 2 * padding )

        yScale : ContinuousScale
        yScale =
            Scale.linear ( 0, 5 ) ( h - 2 * padding, 0 )

        opts : Axis.Options a
        opts =
            Axis.defaultOptions

        xAxis : Svg msg
        xAxis =
            Axis.axis { opts | orientation = Axis.Bottom, tickCount = List.length model } xScale

        yAxis : Svg msg
        yAxis =
            Axis.axis { opts | orientation = Axis.Left, tickCount = 5 } yScale

        areaGenerator : ( Date, Float ) -> Maybe ( ( Float, Float ), ( Float, Float ) )
        areaGenerator ( x, y ) =
            Just ( ( Scale.convert xScale x, Tuple.first (Scale.rangeExtent yScale) ), ( Scale.convert xScale x, Scale.convert yScale y ) )

        lineGenerator : ( Date, Float ) -> Maybe ( Float, Float )
        lineGenerator ( x, y ) =
            Just ( Scale.convert xScale x, Scale.convert yScale y )

        area : String
        area =
            List.map areaGenerator model
                |> Shape.area Shape.monotoneInXCurve

        line : String
        line =
            List.map lineGenerator model
                |> Shape.line Shape.monotoneInXCurve
    in
        svg [ width (toString w ++ "px"), height (toString h ++ "px") ]
            [ g [ transform ("translate(" ++ toString (padding - 1) ++ ", " ++ toString (h - padding) ++ ")") ]
                [ xAxis ]
            , g [ transform ("translate(" ++ toString (padding - 1) ++ ", " ++ toString padding ++ ")") ]
                [ yAxis ]
            , g [ transform ("translate(" ++ toString padding ++ ", " ++ toString padding ++ ")"), class "series" ]
                [ Svg.path [ d area, stroke "none", strokeWidth "3px", fill "rgba(255, 0, 0, 0.54)" ] []
                , Svg.path [ d line, stroke "red", strokeWidth "3px", fill "none" ] []
                ]
            ]



-- From here onwards this is simply example boilerplate.
-- In a real app you would load the data from a server and parse it, perhaps in
-- a separate module.


main =
    view model



-- Here we simply define the data inline. The examples don't include logic for fetching and parsing this data.


model =
    [ ( Date.fromTime 1483521850, 0.107 )
    , ( Date.fromTime 1483521850, 0.266 )
    , ( Date.fromTime 1483521850, 0.285 )
    , ( Date.fromTime 1483521827, 0.084 )
    , ( Date.fromTime 1483521751, 0.081 )
    , ( Date.fromTime 1483521649, 0.073 )
    , ( Date.fromTime 1483521635, 0.049 )
    , ( Date.fromTime 1483521699, 0.359 )
    , ( Date.fromTime 1483521698, 0.089 )
    , ( Date.fromTime 1483521688, 0.146 )
    , ( Date.fromTime 1483521658, 0.112 )
    , ( Date.fromTime 1483521657, 0.055 )
    , ( Date.fromTime 1483521858, 0.069 )
    , ( Date.fromTime 1483521842, 0.049 )
    , ( Date.fromTime 1483521750, 0.126 )
    , ( Date.fromTime 1483521747, 0.1 )
    , ( Date.fromTime 1483521746, 0.132 )
    , ( Date.fromTime 1483521650, 0.063 )
    , ( Date.fromTime 1483521646, 0.059 )
    , ( Date.fromTime 1483521659, 0.078 )
    , ( Date.fromTime 1483521881, 0.054 )
    , ( Date.fromTime 1483521820, 0.057 )
    , ( Date.fromTime 1483521729, 0.167 )
    , ( Date.fromTime 1483521753, 0.063 )
    , ( Date.fromTime 1483521661, 0.057 )
    , ( Date.fromTime 1483521863, 0.061 )
    , ( Date.fromTime 1483521750, 0.07 )
    , ( Date.fromTime 1483521745, 0.126 )
    , ( Date.fromTime 1483521722, 0.126 )
    , ( Date.fromTime 1483521642, 0.436 )
    , ( Date.fromTime 1483521628, 0.068 )
    , ( Date.fromTime 1483521679, 0.067 )
    ]
@rtfeldman

This comment has been minimized.

Show comment
Hide comment
@rtfeldman

rtfeldman Jan 16, 2017

Member

Yikes, that's...a lot. 😅

Could you get that down to a SSCCE?

Member

rtfeldman commented Jan 16, 2017

Yikes, that's...a lot. 😅

Could you get that down to a SSCCE?

@mgold

This comment has been minimized.

Show comment
Hide comment
@mgold

mgold Jan 16, 2017

Contributor

The SVG path says it's expecting a number but given a string. This is odd, because paths are supposed to be strings. But what's even more interesting is that NaN appears in the string. I've gotten DOM errors in D3 (a JS library) when doing the same thing, so that's likely something to focus on. You can obtain NaN in Elm with 0/0 (verify with isNaN).

Contributor

mgold commented Jan 16, 2017

The SVG path says it's expecting a number but given a string. This is odd, because paths are supposed to be strings. But what's even more interesting is that NaN appears in the string. I've gotten DOM errors in D3 (a JS library) when doing the same thing, so that's likely something to focus on. You can obtain NaN in Elm with 0/0 (verify with isNaN).

@jackalcooper

This comment has been minimized.

Show comment
Hide comment
@jackalcooper

jackalcooper Jan 16, 2017

@rtfeldman Sorry, probably not. Because this is based on the example @gampleman wrote. So to reproduce the error it requires the boilerplate of the visualization library. The only difference between his and mine is the model = [...] part. Anyway I would try to downsize it for you.

jackalcooper commented Jan 16, 2017

@rtfeldman Sorry, probably not. Because this is based on the example @gampleman wrote. So to reproduce the error it requires the boilerplate of the visualization library. The only difference between his and mine is the model = [...] part. Anyway I would try to downsize it for you.

@jackalcooper

This comment has been minimized.

Show comment
Hide comment
@jackalcooper

jackalcooper Jan 16, 2017

@mgold @gampleman I found that the exception seems to appear when there are three same dates back to back in a row. Like this one:

 ( Date.fromTime 1483521850, 0.107 )
    , ( Date.fromTime 1483521850, 0.266 )
    , ( Date.fromTime 1483521850, 0.285 )

jackalcooper commented Jan 16, 2017

@mgold @gampleman I found that the exception seems to appear when there are three same dates back to back in a row. Like this one:

 ( Date.fromTime 1483521850, 0.107 )
    , ( Date.fromTime 1483521850, 0.266 )
    , ( Date.fromTime 1483521850, 0.285 )
@gampleman

This comment has been minimized.

Show comment
Hide comment
@gampleman

gampleman Jan 16, 2017

So there are three things going on here:

  1. The SVG library throws a runtime exception when passed an invalid parameter to the d attribute. This violates the "no-runtime exceptions" promise. AFAIK there is no easy solution to this problem, but I'm trying to get some momentum going for a more type safe SVG library (see elm-dev for that, although the current proposal wouldn't solve this problem).

  2. elm-visualization produces this invalid data. Let's tackle that in gampleman/elm-visualization#3.

  3. The underlying cause is that float math is unsafe for this purpose and can produce NaN values. Some of this is tracked in https://github.com/elm-lang/core/issues/721.

gampleman commented Jan 16, 2017

So there are three things going on here:

  1. The SVG library throws a runtime exception when passed an invalid parameter to the d attribute. This violates the "no-runtime exceptions" promise. AFAIK there is no easy solution to this problem, but I'm trying to get some momentum going for a more type safe SVG library (see elm-dev for that, although the current proposal wouldn't solve this problem).

  2. elm-visualization produces this invalid data. Let's tackle that in gampleman/elm-visualization#3.

  3. The underlying cause is that float math is unsafe for this purpose and can produce NaN values. Some of this is tracked in https://github.com/elm-lang/core/issues/721.

@jackalcooper

This comment has been minimized.

Show comment
Hide comment
@jackalcooper

jackalcooper Jan 16, 2017

Thank you @gampleman . I'm going to close this issue since elm-visualization produces the invalid data.

jackalcooper commented Jan 16, 2017

Thank you @gampleman . I'm going to close this issue since elm-visualization produces the invalid data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment