Just working through the samples, and got the exercise about creating 2 random dice and rolling them with a button.
http://guide.elm-lang.org/architecture/effects/random.html
So I thought I would create the dice as a module, remove the roll action, and just have it create a D6 value on init.
So my code is now as follows (should open direct in elm-reactor)
module Components.DiceRoller exposing (Model, Msg, init, update, view)
import Html exposing (..)
import Html.App as Html
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Random
import String exposing (..)
main =
Html.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
-- MODEL
type alias Model =
{ dieFace : Int
}
init : ( Model, Cmd Msg )
init =
( Model 0, (Random.generate NewFace (Random.int 1 6)) )
-- UPDATE
type Msg
= NewFace Int
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NewFace newFace ->
( Model newFace, Cmd.none )
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
-- VIEW
dieFaceImage : Int -> String
dieFaceImage dieFace =
concat [ "/src/img/40px-Dice-", (toString dieFace), ".svg.png" ]
view : Model -> Html Msg
view model =
let
imagePath =
dieFaceImage model.dieFace
in
div []
[ img [ src imagePath ] []
, span [] [ text imagePath ]
]
The problem with this is that it is always producing the same value. I thought I had a problem with the seed to begin with, but if you change
init =
( Model 0, (Random.generate NewFace (Random.int 1 6)) )
init =
( Model 0, (Random.generate NewFace (Random.int 1 100)) )
it works exactly as intended. So it looks like the default generator is not working with small values, seems to work as low down as 10.
The odd thing is this, in this example (which i started with) http://guide.elm-lang.org/architecture/effects/random.html , it works fine with 1-6 when it's not in init.
So my question is, am I doing something wrong, or is this just a wrinkle in elm? Is my usage of the command in init ok?
In the end, I put this in to get the desired effect, which feels wonky.
init =
( Model 0, (Random.generate NewFace (Random.int 10 70)) )
with
NewFace newFace ->
( Model (newFace // 10), Cmd.none )
This must to have something to do with seeding. You're not specifying any value for seed so the generator is defaulting to use the current time.
I think you tried to refresh your page for a few times in a few seconds and you didn't see the value change. If you wait for longer (roughly a minute) you'll see your value change.
I had a look at the source code of Random and I suspect that for seed values that are close enough the first value generated in the range [1,6] doesn't change. I'm not sure whether this is expected or not, probably it's worth raising an issue on GitHub
Related
I want to create a binding of the Plotly.js library to Fable.
I am looking at this js code
import React from 'react';
import Plot from 'react-plotly.js';
class App extends React.Component {
render() {
return (
<Plot
data={[
{
x: [1, 2, 3],
y: [2, 6, 3],
type: 'scatter',
mode: 'lines+points',
marker: {color: 'red'},
},
{type: 'bar', x: [1, 2, 3], y: [2, 5, 3]},
]}
layout={ {width: 320, height: 240, title: 'A Fancy Plot'} }
/>
);
}
}
and my (faulty) attempt of creating a simple test binding looks like this
open Fable.Core
open Fable.Core.JsInterop
open Browser.Types
open Fable.React
// module Props =
type Chart =
|X of int list
|Y of int List
|Type of string
type IProp =
| Data of obj list
let inline plot (props: IProp) : ReactElement =
ofImport "Plot" "react-plotly.js" props []
let myTrace = createObj [
"x" ==> [1,2,3]
"y" ==> [2,6,3]
"type" ==> "scatter"
"mode" ==> "lines"
]
let myData = Data [myTrace]
let testPlot = plot myData
But obviously it does not work. How do I get it to work? Also, what does {[...]} mean? I am new to Javascript, and as far as I know {...} denotes an object which must contain name value pairs, and [...] denotes an array. So {[...]} seems to denote an object with a single nameless member that is an array, but as far as I know, there are no objects with nameless members.
I have been able to reproduce the example you linked. Please note that I don't Plotly and that I went the empiric way and so things can probably be improved :)
I have created the code as I would probably have done it if I had to use it in my production app. So there is a bit more code than in your question because I don't use createObj.
If you don't like the typed DSL you can always simplify it, remove it and use createObj or anonymous record like I did for the marker property :)
You need to install both react-plotly.js plotly.js in your project.
open Fable.Core.JsInterop
open Fable.Core
open Fable.React
// Define props using DUs this helps create a typed version of the React props
// You can then transform a list of props into an object using `keyValueList`
[<RequireQualifiedAccess>]
type LayoutProps =
| Title of string
| Width of int
| Height of int
// GraphType is marked as a `StringEnum` this means
// the value will be replace at compile time with
// their string representation so:
// `Scatter` becomes `"scatter"`
// You can customise the output by using `[<CompiledName("MyCustomName")>]
[<RequireQualifiedAccess; StringEnum>]
type GraphType =
| Scatter
| Bar
[<RequireQualifiedAccess; StringEnum>]
type GraphMode =
| Lines
| Points
| Markers
| Text
| None
[<RequireQualifiedAccess>]
type DataProps =
| X of obj array
| Y of obj array
| Type of GraphType
| Marker of obj
// This is an helpers to generate the `flagList` waited by Plotly, if you don't like it you can just remove
// member and replace it with `| Mode of string` and so you have to pass the string by yourself
static member Mode (modes : GraphMode seq) : DataProps =
let flags =
modes
|> Seq.map unbox<string> // This is safe to do that because GraphMode is a StringEnum
|> String.concat "+"
unbox ("mode", flags)
[<RequireQualifiedAccess>]
type PlotProps =
| Nothing // Should have real props here is there exist more than Data and Layout
// Here notes that we are asking for an `Array` or Data
// Array being the type expected by the JavaScript library
// `DataProps seq` is our way to represents props
static member Data (dataList : (DataProps seq) array) : PlotProps =
let datas =
dataList
|> Array.map (fun v ->
keyValueList CaseRules.LowerFirst v // Transform the list of props into a JavaScript object
)
unbox ("data", datas)
static member Layout (props : LayoutProps seq) : PlotProps =
unbox ("layout", keyValueList CaseRules.LowerFirst props)
// All the example I saw from react-plotly was using this factory function to transform the plotly library into a React component
// Even, the example you shown if you look at the Babel tab in the live example
let createPlotlyComponent (plotly : obj) = import "default" "react-plotly.js/factory"
// Immport the plotly.js library
let plotlyLib : obj = import "default" "plotly.js"
// Apply the factory on the plotly library
let Plot : obj = createPlotlyComponent plotlyLib
// Helper function to instantiate the react components
// This is really low level, in general we use `ofImport` like you did but if I use `ofImport` then I got a React error
let inline renderPlot (plot : obj) (props : PlotProps list) =
ReactBindings.React.createElement(plot, (keyValueList CaseRules.LowerFirst props), [])
let root =
// Here we can render the plot using our Typed DSL
renderPlot
Plot
[
PlotProps.Data
[|
[
DataProps.X [| 1; 2; 3 |]
DataProps.Y [| 2; 6; 3 |]
DataProps.Type GraphType.Scatter
DataProps.Mode
[
GraphMode.Lines
GraphMode.Points
]
DataProps.Marker {| color = "red" |}
]
[
DataProps.Type GraphType.Bar
DataProps.X [| 1; 2; 3 |]
DataProps.Y [| 2; 5; 3 |]
]
|]
PlotProps.Layout
[
LayoutProps.Width 640
LayoutProps.Height 480
LayoutProps.Title "A Fancy Plot"
]
]
I'm a bit late to the party here, but wanted to give you a different option if you're still looking to use plotly.js with Fable.
I've been working on bindings for plotly.js for the past month or so, and it's in a pretty usable state as of now. That being said, I wouldn't say it's production ready.
This is what the example you want to convert would look like written with Feliz.Plotly:
open Feliz
open Feliz.Plotly
let chart () =
Plotly.plot [
plot.traces [
traces.scatter [
scatter.x [ 1; 2; 3 ]
scatter.y [ 2; 6; 3 ]
scatter.mode [
scatter.mode.lines
scatter.mode.markers
]
scatter.marker [
marker.color color.red
]
]
traces.bar [
bar.x [ 1; 2; 3 ]
bar.y [ 2; 5; 3 ]
]
]
plot.layout [
layout.width 320
layout.height 240
layout.title [
title.text "A Fancy Plot"
]
]
]
You can find more information out here.
I get input JSON data from JS. This is a simple object, among which there is a date in the format "DD.MM.YYYY" - just a string.
If there is no dateStart in the object, I have to replace it with the current date (in withDefault).
paramsDecoder : Decode.Decoder Params
paramsDecoer =
Decode.succeed Params
|> Decode.andMap (Decode.field "dateStart" (Decode.string) |> (Decode.withDefault) "")
|> Decode.andMap (Decode.field "dateEnd" (Decode.string) |> (Decode.withDefault) "")
|> Decode.andMap (Decode.field "country" (Decode.string) |> (Decode.withDefault) "spain")
How can I do this in ELM?
Timezone is not important and is always equal to the same region.
I found an example of Time.now Time.zone usage,
but there time is getting in Update and its too late.
I solved this in two parts:
Part 1
Send the current time in to Elm when it initializes, using flags:
Elm.Main.init({flags: Date.now()});
And put it in your model:
import Time exposing (Posix, millisToPosix)
type alias Model = { now : Time.Posix, ... }
init : Int -> (Model, Cmd Msg)
init time = (Model (millisToPosix time), Cmd.none)
For your use case, you can then use withDefault model.now.
Part 2
The solution in Part 1 will only set now to the time when the page is loaded.
If you want to keep an up to date time you can use Time.every to update your model:
import Time exposing (Posix, millisToPosix, every)
timeOutCheck : Sub Msg
timeOutCheck = Time.every 250 UpdateTimeNow
type Msg = UpdateTimeNow Posix | ...
update msg model = case msg of
UpdateTimeNow time = { model | now = time }
...
This will ensure now is never more than 250 ms behind the current time. You can change 250 to suit your need.
According to this answer and its helpful Ellie I now have an idea of how we get the current time in Elm 0.18.
I want to get the current time and then post it in JSON to my server. I already have a POST working with a hardcoded timestamp, but I just don't see how to get the current time and then include it in the JSON I am POSTing.
My guess is that I need to chain a couple of commands (get current time, make a POST to server).
[I have also read another SO which shows how you can run a few commands in a row by directly calling the update function, which sounds like a nice idea but I am not sure if it is what I need for my situation.]
Experiment ([edit] perhaps more of a distraction than was intended)
In order to get my head around this I thought I would try to solve a similar problem that I can more easily set up in Ellie.
In the original Ellie the current time is gotten, updated into the model, and then the view function shows it.
In my version I wanted this to be a two-step process and therefore have grown the model to be a Maybe Float for the time and a String message. The view function shows the message string and a button -- the plan is that when the button is pressed it tells the runtime to 'go get the current time and then copy it across into the message slot'.
If I can solve this problem then I feel like I can solve my original problem.
My Ellie does not do this yet. When you press the button the time is gotten and is put into the time slot in the model, but I do not know how to tell the runtime to '...now copy that time across into the message slot'. The PutTimeInMessage message is in place, but I don't know how to get it to run after the GetCurrentTime message/command.
Any suggestions?
Here is my code so far (it compiles), which you can run here in Ellie:
module Main exposing (..)
import Html exposing (..)
import Html.Events exposing (..)
import Time exposing (Time)
import Date
import Task
type alias Model =
{ time : Maybe Float
, message : String
}
type Msg
= OnTime Time
| GetCurrentTime
| PutTimeInMessage
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
OnTime t ->
( { model | time = Just t }, Cmd.none )
GetCurrentTime ->
( model, getTime )
PutTimeInMessage ->
case model.time of
Nothing ->
( model, Cmd.none )
Just t ->
( { model | message = toString t }, Cmd.none )
view : Model -> Html Msg
view model =
div []
[ div []
[ button [ onClick GetCurrentTime ] [ Html.text "Get now time." ]
]
, model.message |> Html.text
]
getTime : Cmd Msg
getTime =
Time.now
|> Task.perform OnTime
main =
Html.program
{ init = ( Model Nothing "Empty message.", Cmd.none )
, update = update
, view = view
, subscriptions = always Sub.none
}
The way I see it, you can just update message field along with time field, when OnTime message is received. Thus, the whole update function is going to look like this:
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
OnTime t ->
( { model | time = Just t, message = toString t }, Cmd.none )
GetCurrentTime ->
( model, getTime )
The message is set in OnTime action, because in the GetCurrentTime time is unknown and is known only after getTime function is perform and OnTime message is received.
If you still want to use a separate action for putting the message, then the following code is the option:
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
OnTime t ->
update PutTimeInMessage { model | time = Just t }
GetCurrentTime ->
( model, getTime )
PutTimeInMessage ->
case model.time of
Nothing ->
( model, Cmd.none )
Just t ->
( { model | message = toString t }, Cmd.none )
But to be honest, the most preferable solution, would be just displaying the time in the view differently, so you don't need the message field (but probably I don't see the whole picture):
view : Model -> Html Msg
view model =
div []
[ div []
[ button [ onClick GetCurrentTime ] [ Html.text "Get now time." ]
]
, viewTime model.time
]
viewTime : Maybe Float -> Html Msg
viewTime time =
case time of
Nothing -> Html.text "Empty message."
Just t -> Html.text (toString t)
I came across an SO which explained how to do a sequence of Http requests with Task.andThen. Since I can see the type of Time.now is a Task I figured that I could adapt that example for my purposes if I use Http.toTask.
Below is the solution I came up with and here it is in Ellie:
module Main exposing (..)
import Html exposing (..)
import Html.Events exposing (..)
import Http
import Json.Decode as JD
import Json.Encode as JE
import Task
import Time
type alias Model =
{ url : String
}
type Msg
= PostTimeToServer
| PostDone (Result Http.Error String)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
PostTimeToServer ->
( model, postTimeToServer model.url )
PostDone _ ->
( model, Cmd.none )
view : Model -> Html Msg
view model =
div []
[ div []
[ button [ onClick PostTimeToServer ] [ Html.text "POST the current time." ]
]
]
postTimeToServer : String -> Cmd Msg
postTimeToServer url =
let
getTime =
Time.now
postTime t =
JD.string
|> Http.post url (JE.float t |> Http.jsonBody)
|> Http.toTask
request =
getTime <<-- Here is
|> Task.andThen postTime <<-- the key bit.
in
Task.attempt PostDone request
main =
Html.program
{ init = ( Model "url_here", Cmd.none )
, update = update
, view = view
, subscriptions = always Sub.none
}
I've been toying with Effects.tick and Time in one of my apps, and I can't seem to get my delays to be around the Time.second that it claims to be. It's nearly immediate. I understand that type alias Time = Float, and from my logs it seems that second = 1000, but this just burns so quickly, even with logging. Is there a clear explanation for this?
Effects.tick causes an action to be called nearly instantaneously, but the action that is being called gets passed a value of the current time of the tick. If you want to delay something by a second while using Effects.tick, you'll have to keep track of a starting point and compare it to the time of the current tick, and that's where you can add in Time.second.
Take this arbitrary example (you can paste it into http://elm-lang.org/try):
import Html exposing (..)
import Html.Events exposing (..)
import Html.Attributes exposing (..)
import StartApp
import Effects exposing (Never)
import Task
import Signal
import Time exposing (..)
app =
StartApp.start
{ init = init
, view = view
, update = update
, inputs = [ ]
}
main =
app.html
type alias Model =
{ lastTick : Maybe Time
, tickEverySecond : Maybe Time
}
init =
({ lastTick = Nothing, tickEverySecond = Nothing }, Effects.tick MyTicker)
view address model =
div []
[ div [] [ text <| "Current tick: " ++ (toString model.lastTick) ]
, div [] [ text <| "Updated every second: " ++ (toString model.tickEverySecond) ]
]
type Action
= MyTicker Time
update action model =
let
everySecond = Maybe.withDefault 0 model.tickEverySecond
getTickEverySecond time =
if time > everySecond + Time.second then
Just time
else
Just everySecond
in
case action of
MyTicker time -> (
{ model | lastTick = Just time
, tickEverySecond = getTickEverySecond time
}, Effects.tick MyTicker)
port tasks : Signal (Task.Task Never ())
port tasks =
app.tasks
Every update call requests a new tick, so it will just spin forever. The important part is that tickEverySecond is only updated if the last time it was updated is greater than one second ago.
You ask about the time precision. If you run that example, you'll notice that the increment every second is somewhat approximate; it will drift greater than a second. That isn't due to any kind of underlying imprecision. Remember that Elm's Time functionality is a thin veneer over Javascript's time and timer functionality. That drift is merely an artifact of all the tiny little delays caused by running code that responds to a timer signal.
Using Mac OS X API, I'm trying to save a PDF file with a Quartz filter applied, just like it is possible from the "Save As" dialog in the Preview application. So far I've written the following code (using Python and pyObjC, but it isn't important for me):
-- filter-pdf.py: begin
from Foundation import *
from Quartz import *
import objc
page_rect = CGRectMake (0, 0, 612, 792)
fdict = NSDictionary.dictionaryWithContentsOfFile_("/System/Library/Filters/Blue
\ Tone.qfilter")
in_pdf = CGPDFDocumentCreateWithProvider(CGDataProviderCreateWithFilename ("test
.pdf"))
url = CFURLCreateWithFileSystemPath(None, "test_out.pdf", kCFURLPOSIXPathStyle,
False)
c = CGPDFContextCreateWithURL(url, page_rect, fdict)
np = CGPDFDocumentGetNumberOfPages(in_pdf)
for ip in range (1, np+1):
page = CGPDFDocumentGetPage(in_pdf, ip)
r = CGPDFPageGetBoxRect(page, kCGPDFMediaBox)
CGContextBeginPage(c, r)
CGContextDrawPDFPage(c, page)
CGContextEndPage(c)
-- filter-pdf.py: end
Unfortunalte, the filter "Blue Tone" isn't applied, the output PDF looks exactly as the input PDF.
Question: what I missed? How to apply a filter?
Well, the documentation doesn't promise that such way of creating and using "fdict" should cause that the filter is applied. But I just rewritten (as far as I can) sample code /Developer/Examples/Quartz/Python/filter-pdf.py, which was distributed with older versions of Mac (meanwhile, this code doesn't work too):
----- filter-pdf-old.py: begin
from CoreGraphics import *
import sys, os, math, getopt, string
def usage ():
print '''
usage: python filter-pdf.py FILTER INPUT-PDF OUTPUT-PDF
Apply a ColorSync Filter to a PDF document.
'''
def main ():
page_rect = CGRectMake (0, 0, 612, 792)
try:
opts,args = getopt.getopt (sys.argv[1:], '', [])
except getopt.GetoptError:
usage ()
sys.exit (1)
if len (args) != 3:
usage ()
sys.exit (1)
filter = CGContextFilterCreateDictionary (args[0])
if not filter:
print 'Unable to create context filter'
sys.exit (1)
pdf = CGPDFDocumentCreateWithProvider (CGDataProviderCreateWithFilename (args[1]))
if not pdf:
print 'Unable to open input file'
sys.exit (1)
c = CGPDFContextCreateWithFilename (args[2], page_rect, filter)
if not c:
print 'Unable to create output context'
sys.exit (1)
for p in range (1, pdf.getNumberOfPages () + 1):
#r = pdf.getMediaBox (p)
r = pdf.getPage(p).getBoxRect(p)
c.beginPage (r)
c.drawPDFDocument (r, pdf, p)
c.endPage ()
c.finish ()
if __name__ == '__main__':
main ()
----- filter-pdf-old.py: end
=======================================================================
The working code based on the answer:
from Foundation import *
from Quartz import *
pdf_url = NSURL.fileURLWithPath_("test.pdf")
pdf_doc = PDFDocument.alloc().initWithURL_(pdf_url)
furl = NSURL.fileURLWithPath_("/System/Library/Filters/Blue Tone.qfilter")
fobj = QuartzFilter.quartzFilterWithURL_(furl)
fdict = { 'QuartzFilter': fobj }
pdf_doc.writeToFile_withOptions_("test_out.pdf", fdict)
two approaches - if you need to open and modify an already existing file, use the PDFKit's PDFDocument (reference) and use PDFDocument's writeToFile_withOptions_ with option dict including the "QuartzFilter" option of needed filter.
OTOH if you need your own drawing and have a CGContext at hand, you can use something along these lines:
from Quartz import *
data = NSMutableData.dataWithCapacity_(1024**2)
dataConsumer = CGDataConsumerCreateWithCFData(data)
context = CGPDFContextCreate(dataConsumer, None, None)
f = QuartzFilter.quartzFilterWithURL_(NSURL.fileURLWithPath_("YourFltr.qfilter"))
f.applyToContext_(context)
# do your drawing
CGPDFContextClose(context)
# the PDF is in the data variable. Do whatever you need to do with the data (save to file...).