Lisplog

Blogging in Lisp

Search

Feed Aggregator Page 659

Rendered on Sun, 11 Oct 2020 15:03:23 GMT  newer latest older 

Do you put tests outside the `tests/` folder?

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)

Reasons that people were forced to move from Elm to something else?

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.)

Reasons that people were forced to move from Elm to something else?

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.)

Parsers with Error Recovery

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.

Do you put tests outside the `tests/` folder?

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?

Set default values while decoding JSON

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!

Do you put tests outside the `tests/` folder?

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:

  • ChiSquared.elm // the module
  • ChiSquared.spec.elm // the specifications (unit tests) for the module

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

Alexander Artemenko: cl-cont

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! :)

Can the compiler skip virtual DOM?

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.

Package install timeout 0.19.1

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.

Set default values while decoding JSON

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:

Can we create Mobile apps in elm?

via Elm - Latest posts by @Laurent Laurent Payot on Sat, 10 Oct 2020 07:55:05 GMT

WebRTC conference sample with custom elements

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.

Set default values while decoding JSON

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.)

Set default values while decoding JSON

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)
            )
        )

Can we create Mobile apps in elm?

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 :smiley:

Set default values while decoding JSON

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 :slight_smile:

Set default values while decoding JSON

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!

Can we create Mobile apps in elm?

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/

Package install timeout 0.19.1

via Elm - Latest posts by @Muhmmmad_Bilal Muhmmmad Bilal on Fri, 09 Oct 2020 16:01:02 GMT

yes, it is downloaded

 newer latest older