Feed Aggregator Page 659
Rendered on Sun, 11 Oct 2020 15:03:23 GMT
Rendered on Sun, 11 Oct 2020 15:03:23 GMT
via Elm - Latest posts by @grigorious on Sun, 11 Oct 2020 14:19:07 GMT
Currently the commands looks like:
elm-test src/**/ChiSquared.spec.elm
To get all files that need to be tested I’ve used glob. Some time ago I wrote a CLI tool (using typescript) to automate some angular processes - and it was general enough to work with elm.
(the basic idea was that some modules - which in my case deal with math can be easily moved with test attached)
P.S. I consider my approach a hack - but since the number of modules that I test is fairly small it generally works ok.
P.P.S. Angular applications have a config file (or more generally they use a typescript config file) to tell which files you want to test, it looks like this:
…
“include”: [
“src/**/*.spec.ts”,
“src/**/*.d.ts”
]
…
The above is the default, that is usually never changed, since the angular CLI tool automatically creates .spec.ts
files. (I like the glob approach, because it allows you to organise your code in both the modular way and in the more traditional “test” folder way)
via Elm - Latest posts by @jhbrown Jeremy H. Brown on Sun, 11 Oct 2020 14:04:33 GMT
(To be clear, the goal here is to identify organizational motivations/pathologies that cause organizations to decide to move off of Elm, not to pick on Elm.)
via Elm - Latest posts by @jhbrown Jeremy H. Brown on Sun, 11 Oct 2020 14:02:51 GMT
Over on Slack, someone posted in #jobs a that “We have developed a version of our app in Elm and want to change the framework to React to take the app to the next level.” This prompted a really long and interesting discussion of reasons that people have been forced (typically by management, rather than technology) to rewrite Elm code as something non-Elm. I’m opening this thread here to provide a place to capture those stories in a more permanent medium than Slack. (I’m not going to start by transcribing everyone’s comments from Slack – that wouldn’t feel right – but hoping we’ll catch the same stories and more over here.)
via Elm - Latest posts by @system system on Sun, 11 Oct 2020 13:09:51 GMT
This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
via Elm - Latest posts by @lydell Simon Lydell on Sun, 11 Oct 2020 11:54:12 GMT
@grigorious Cool! Can you share the CLI command you use to run the tests?
via Elm - Latest posts by @iwtilma on Sun, 11 Oct 2020 11:53:18 GMT
Thanks a lot, lydell, for elaborating on the subject. It is helping me a lot!
via Elm - Latest posts by @grigorious on Sun, 11 Oct 2020 11:26:14 GMT
I like putting test file next to the code file.
(fictional) example:
This makes it easier to find the test and also lets you know which files (modules) have tests at a glance. (I first encountered this developing angular applications, the reasoning was - that modules should be self contained - and easy to move into libraries or just copy/pasted into other applications). More info on the angular team reasoning can be found here: https://angular.io/guide/testing
via Planet Lisp by on Sat, 10 Oct 2020 20:52:05 GMT
This is a pretty old system which implements Delimited Continuations for Common Lisp. Initially, it was part of the Weblocks web-framework.
Sadly, but cl-cont has no documentation. I found only one example on this page.
It always was hard to wrap my mind around continuations. Probably that is why I decided to remove their support from the core of the Weblocks when I did the refactoring.
Now it is time to dive into continuations and probably to return them to Weblocks as an additional library.
Let's see what continuation is and how they can be used in practice!
The first thing to note is that each piece of code which uses this magic should be wrapped into with-call/cc
. The second thing to remember is that let/cc
form allows you to capture the moment and to save the execution point somewhere.
The code below prints three lines. It prints "Begin", then captures the execution point, prints "Before returning" and returns the captured point:
POFTHEDAY> (cont:with-call/cc
(format t "Begin~%")
(cont:let/cc k
(format t "Before returning k~%")
k)
(format t "End~%")
:final-result)
Begin
Before returning k
#<FUNCTION (LAMBDA (&OPTIONAL #:G1455 &REST #:G1456)) {22A10A0B}>
What has happened to our third print "End"? It didn't have a chance to be executed yet. But we can continue execution, by calling the function we've received as the result on the previous code snippet:
POFTHEDAY> (funcall *)
End
:FINAL-RESULT
POFTHEDAY> (funcall **)
End
:FINAL-RESULT
That is why it is called "continuation"! Yeah! As you can see, we can call this captured function any amount of times.
Now, let's try to create a function which will interrupt its execution and return a continuation.
Our first attempt might be like this:
POFTHEDAY> (defun foo ()
(cont:with-call/cc
(format t "Begin foo~%")
(cont:let/cc k
(format t "Before returning k from foo~%")
k)
(format t "End foo~%")
:final-result))
POFTHEDAY> (cont:with-call/cc
(format t "Before foo~%")
(foo)
(format t "After foo~%"))
Before foo
Begin foo
Before returning k from foo
After foo ;; Ups! I've expected it will not output this
NIL ;; and return a continuation function instead of NIL!
As you can see, only half of our function was executed and then control flow continued, printed "After foo" and finished without giving us any continuation to play with :(
To make this code work as expected, we need to move with-call/cc
form and make it wrap the function definition:
POFTHEDAY> (cont:with-call/cc
(defun foo-wrapped ()
(format t "Begin foo~%")
(cont:let/cc k
(format t "Before returning k from foo~%")
k)
(format t "End foo~%")
:final-result))
POFTHEDAY> (cont:with-call/cc
(format t "Before foo~%")
(foo-wrapped)
(format t "After foo~%"))
Before foo
Begin foo
Before returning k from foo
#<CLOSURE (LAMBDA (&OPTIONAL #:G1561 &REST #:G1562)) {10067F637B}>
This version works exactly as I've expected. It halts execution inside the foo
's call and returns this continuation.
Now we can call continuation to continue computation of the foo function and the rest of our top-level form:
POFTHEDAY> (funcall *)
End foo
After foo
NIL
The latter case works because cont:with-call/cc
is smart enough and if it wraps the function foo-wrapped
into a special funcallable object:
;; This function is usual:
POFTHEDAY> (fdefinition 'foo)
#<FUNCTION FOO>
;; But this one is not.
;; It supports nested continuations:
POFTHEDAY> (fdefinition 'foo-wrapped)
#<CL-CONT::FUNCALLABLE/CC {10063435FB}>
Now let's adapt some examples from this Wikipedia article about continuations. The first example shows how to save continuation into the global variable and what happens when you use the same function to create the second continuation:
POFTHEDAY> (defvar *the-continuation*)
POFTHEDAY> (cont:defun/cc test ()
(let ((i 0))
;; let/cc binds to k symbol a variable representing
;; this point in the program as the argument to
;; that function.
;;
;; In this case, we assigns that
;; continuation to the variable *the-continuation*
;; and then return the incremented value of 'i'.
;;
(cont:let/cc k
(setf *the-continuation* k)
(incf i))
;; The next time *the-continuation* is called,
;; we start here:
(incf i)))
POFTHEDAY> (test)
1
POFTHEDAY> (funcall *the-continuation*)
2
POFTHEDAY> (funcall *the-continuation*)
3
;; Stores the current continuation (which will print 4 next) away
POFTHEDAY> (defparameter *another-continuation* *the-continuation*)
;; Resets *the-continuation*:
POFTHEDAY> (test)
1
POFTHEDAY> (funcall *the-continuation*)
2
;; Uses the previously stored continuation:
POFTHEDAY> (funcall *another-continuation*)
4
The second example is more interesting because it let us create a simple framework for running green threads.
First, we need to define such two primitives: fork
and yield
:
POFTHEDAY> (defparameter *queue* nil)
POFTHEDAY> (defun empty-queue? ()
(null *queue*))
POFTHEDAY> (defun enqueue (func)
(setf *queue*
(append *queue*
(list func))))
POFTHEDAY> (defun dequeue ()
(pop *queue*))
;; This stops running the current thread by placing it into the queue
;; and starts running a (func).
POFTHEDAY> (cont:defun/cc fork (func &rest args)
(cont:let/cc k
(enqueue k)
(apply func args)))
;; This stops running the current thread by placing it into the queue
;; and starts running the other thread from the queue if there is any:
POFTHEDAY> (cont:defun/cc yield ()
(cont:let/cc k
(enqueue k)
(funcall (dequeue))))
How does fork
function work?
;; This is the function we want to run in "parallel":
POFTHEDAY> (defun do-job ()
(format t "Inside job~%"))
;; Initially, our queue is empty:
POFTHEDAY> *queue*
NIL
;; Now when we'll call the fork,
;; it will:
;;
;; - capture current continuation;
;; - put it into the queue;
;; - execute do-job function.
POFTHEDAY> (cont:with-call/cc
(format t "Before fork~%")
(fork #'do-job)
(format t "After fork~%"))
Before fork
Inside job
NIL
;; Now queue has one function which is
;; the rest of our initial computation.
POFTHEDAY> *queue*
(#<FUNCTION (LAMBDA (&OPTIONAL #:G1655 &REST #:G1656)) {22A1719B}>)
;; When the rest of the computation gets called,
;; it prints "After fork" and exits:
POFTHEDAY> (funcall (dequeue))
After fork
NIL
Yield works similarly. It captures the current continuation, appends it to the queue, takes the next coroutine from the top of the queue and executes it.
To test how two coroutines will behave when running in parallel, let's create a function which will print its name in the loop. On each iteration a coroutine will call yield
to give other coroutines a chance to get executed:
POFTHEDAY> (cont:defun/cc do-stuff-n-print (name)
(loop for n from 1 upto 3
do (format t "~A ~A~%" name n)
(yield)
(sleep 1)))
;; We also need to add this primive to our framework
POFTHEDAY> (defun wait-for-threads ()
(loop
when (empty-queue?)
return nil
do (funcall (dequeue))))
POFTHEDAY> (cont:with-call/cc
(fork #'do-stuff-n-print "Foo")
(fork #'do-stuff-n-print "Bar")
(wait-for-threads))
Foo 1
Bar 2
Foo 3
Bar 1
Foo 2
Bar 3
The result we've got is the same as the result of the Wikipedia article. Messages from both coroutines are interleaving. That is great!
Now, cl-cont does not look so strange to me. It is time to reimplement continuation widgets for the Weblocks! :)
via Elm - Latest posts by @system system on Sat, 10 Oct 2020 15:33:18 GMT
This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
via Elm - Latest posts by @Muhmmmad_Bilal Muhmmmad Bilal on Sat, 10 Oct 2020 11:10:58 GMT
I am using it in WSL Ubuntu … It works on Red hat , I checked. Though Curl has successfully downloaded the zip file by handling redirection.
via Elm - Latest posts by @lydell Simon Lydell on Sat, 10 Oct 2020 09:26:21 GMT
I think I will go with the Dec.succeed, because it keeps the name of the type in the function
Just for full understanding, you can keep “the name of the type” in the function in the map2
situation too:
decodeLocations : Decoder (List Location)
decodeLocations =
Dec.field "locations"
(Dec.list
(Dec.map2 (\id name -> Location id name True)
(Dec.field "id" Dec.int)
(Dec.field "name" Dec.string)
)
)
The map3
version can also be written that way:
decodeLocations : Decoder (List Location)
decodeLocations =
Dec.field "locations"
(Dec.list
(Dec.map3 (\id name checked -> Location id name checked)
(Dec.field "id" Dec.int)
(Dec.field "name" Dec.string)
(Dec.succeed True)
)
)
However, directly calling Location
like that is usually frowned upon. It’s much more clear to just “build the record instead”: { id = id, name = name, checked = checked }
. But it’s good to know that when you make a type alias Foo
for a record (yes, only for records!), you get not only a type named Foo
, Elm also auto-generates a function called Foo
that takes all the stuff in the record in order and builds such a record. It is that auto-generated function that you use in the body of decodeLocations
, while the type with the same name is used in the decodeLocations : Decoder (List Location)
type annotation.
Yet one way of doing it is exploiting the fact that the auto-generated function takes the record contents in a specific order. So you could move checked : Bool
first in the record, then you could do (but I’m not saying it’s a good solution!):
type alias Location =
{ checked : Bool
, name : String
, id : Int
}
decodeLocations : Decoder (List Location)
decodeLocations =
Dec.field "locations"
(Dec.list
(Dec.map2 (Location True)
(Dec.field "id" Dec.int)
(Dec.field "name" Dec.string)
)
)
You don’t need to understand all of those details right now, but it might be handy some time! Either way, I like @Atlewee’s solution best too.
Any suggestions on a good beginners tutorial on Json decoding?
This blog post was enlightening for me at least:
via Elm - Latest posts by @Laurent Laurent Payot on Sat, 10 Oct 2020 07:55:05 GMT
via Elm - Latest posts by @marcw Marc Walter on Sat, 10 Oct 2020 07:17:39 GMT
I hadn’t thought about that, but I would think it is possible.
You could create a web-socket custom element for the server communication. A con would be that you need to store the queue of all outgoing messages in elm and also have to drain the queue using events from the element. Similar to the two-way handshake of tcp. A pro would be that we could handle failing or rejected messages.
Adding ICE candidates and SDP using attributes should work well, right now the user element is always rendered anyway. But then the custom element would own the peer connection state (and storing it in Elm too would not make sense). Definitely makes sense for a reliable solution, but I wanted Elm to hold all state in this example.
via Elm - Latest posts by @iwtilma on Sat, 10 Oct 2020 07:10:21 GMT
Thank you both. Both approaches help me to discover possibilities I would never think of.
Thanks Atlewee to use the <| operator as well, showing me how to use it in this situation.
I think I will go with the Dec.succeed, because it keeps the name of the type in the function.
Any suggestions on a good beginners tutorial on Json decoding? (I read “An Introduction to Elm” of course.)
via Elm - Latest posts by @lydell Simon Lydell on Sat, 10 Oct 2020 00:07:52 GMT
It’s also possible to stick with map2
by replacing Location
with a lambda function:
decodeLocations : Decoder (List Location)
decodeLocations =
Dec.field "locations"
(Dec.list
(Dec.map2 (\id name -> { id = id, name = name, checked = True })
(Dec.field "id" Dec.int)
(Dec.field "name" Dec.string)
)
)
via Elm - Latest posts by @Atlewee Atle Wee Førre on Fri, 09 Oct 2020 22:46:51 GMT
I also use elm wrapped in a pure webview application. Persistant storage, camera, even bluetooth sometimes ++ is avaiable in the browser. But if I need some special stuff only avalable native (rare) I wire up ports directly to native (java or swift) … I can not go back to anything else after getting used to elm-ui
via Elm - Latest posts by @Atlewee Atle Wee Førre on Fri, 09 Oct 2020 22:35:12 GMT
D.field "locations" <| D.list <|
D.map3 Location
( D.field "id" D.int )
( D.field "name" D.string )
( D.succeed True )
You use the .succeed decoder. It does not evaluate anything, but gives a sucessful decoding of whatever value you pass to it
via Elm - Latest posts by @iwtilma on Fri, 09 Oct 2020 21:06:46 GMT
I want to decode JSON, but my type alias needs more information than the JSON provides. I use this type alias:
type alias Location =
{ id : Int
, name : String
, checked : Bool
}
The JSON only contains the fields id and name. The field checked should be True by default. I have no idea where to add this boolean.
So far I have this:
decodeLocations : Decoder (List Location)
decodeLocations =
Dec.field "locations" ( Dec.list ( Dec.map2 Location
( Dec.field "id" Dec.int )
( Dec.field "name" Dec.string )
) )
...
type Msg
= GotLocations ( Result Http.Error ( List Location ) )
This results in the compiler stating a type mismatch:
This `field` call produces:
Decoder (List (Bool -> Location))
But the type annotation on `decodeLocations` says it should be:
Decoder (List Location)
I understand this mismatch, but where could I add the Bool?
Thanks in advance!
via Elm - Latest posts by @ronanyeah on Fri, 09 Oct 2020 20:02:49 GMT
I’ve been writing Progressive Web Apps in Elm for a while. I intend to put this one into app stores next using https://capacitorjs.com/
via Elm - Latest posts by @Muhmmmad_Bilal Muhmmmad Bilal on Fri, 09 Oct 2020 16:01:02 GMT
yes, it is downloaded