Feed Aggregator Page 680
Rendered on Mon, 31 Jan 2022 13:02:33 GMT
Rendered on Mon, 31 Jan 2022 13:02:33 GMT
via Elm - Latest posts by @paulh Paul Hollyer on Mon, 31 Jan 2022 00:36:36 GMT
I don’t think there is an inherent hate for components.
It’s more a case of splitting your code when there is a valid reason for it, rather than starting out automatically trying to come up with a component based architecture.
I’ve learnt from my own mistakes how starting out with an OOP style approach can cause you to run into difficulties - Rupert is trying to prevent people from making those same mistakes, but maybe sometimes it’s better to learn from them than avoid them without understanding why .
‘Components’ are not a bad thing in my view though. For instance, I’ve got a dropdown/select component that I use quite a lot in a current project. The reason I chose to build it as it’s own component is because I want specific behaviour from it, such as keyboard navigation, and as I need this in many places, it makes sense to me for this module to manage it’s own state, so that I don’t have all that replication of code across all the consuming modules (and repeated for every ‘instance’ in every module), and I didn’t want to push that responsibility on to every parent module. I also have the guarantee of the same behaviour everywhere without having to remember to handle all the keyboard events in every parent - so less chance of bug creeping in somewhere.
So I think there is a place for ‘components’, but not for ‘components’ sake.
Edit:
That’s not what anyone is suggesting, it’s simply easier to start out with a single file and single update
function and then break it up when and where it makes sense.
via Elm - Latest posts by @AvailableUsername on Sun, 30 Jan 2022 23:24:38 GMT
I think the Html.map
vs (SmallComponentMsg -> msg)
is really a detail in this discussion at this point.
What I’d like to understand is the reason for this hate for components. Have you read my example of application here: What's wrong with using Html.map? - #13 by AvailableUsername
Do you think you’re going to have an easy time writing such an application with just one big update function for everything?
via Planet Lisp by on Sun, 30 Jan 2022 22:11:10 GMT
#CommonLisp #Lisp
Usually you are not supposed to know whether a piece of Lisp code has been compiled or is being interpreted without compilation. In particular, compiling a Lisp program shouldn't change its semantics in any way, right?
See, there is one thing that surely is an exception to that rule. It is specified in a way that allows it to differ in terms of how it behaves between compiled and non-compiled code: LOAD-TIME-VALUE
, which I recently used to implement STATIC-LET
.
And it's not a nice thing to discover.
CLHS 3.2.2.2 Minimal Compilation states (emphasis mine):
The first argument in a
load-time-value
form in source code processed bycompile
is evaluated at compile time; in source code processed bycompile-file
, the compiler arranges for it to be evaluated at load time. In either case, the result of the evaluation is remembered and used later as the value of theload-time-value
form at execution time.
More, CLHS Special Operator LOAD-TIME-VALUE
states (emphasis mine):
If a
load-time-value
expression is processed bycompile-file
, the compiler performs its normal semantic processing (such as macro expansion and translation into machine code) on form, but arranges for the execution of form to occur at load time in a null lexical environment, with the result of this evaluation then being treated as a literal object at run time. It is guaranteed that the evaluation of form will take place only once when the file is loaded, but the order of evaluation with respect to the evaluation of top level forms in the file is implementation-dependent.If a load-time-value expression appears within a function compiled with
compile
, the form is evaluated at compile time in a null lexical environment. The result of this compile-time evaluation is treated as a literal object in the compiled code.If a load-time-value expression is processed by
eval
, form is evaluated in a null lexical environment, and one value is returned. Implementations that implicitly compile (or partially compile) expressions processed byeval
might evaluate form only once, at the time this compilation is performed.
COMPILE-FILE
and COMPILE
have a "must", where EVAL
only has a "might". This means that functions defined using EVAL
, without compilation, can cause a new object to be instantiated every time, which will both call the initialization form every time the body is entered and break all code that depends on the static binding values to stay static.
So, in order to get the behavior we want (in which an object is only allocated once), the code containing the STATIC-LET
form must be compiled, at which point the load-time values will either be instantiated (in case of COMPILE
) or stored in the resulting FASLs to be instantiated at load time (in case of COMPILE-FILE
).
We can make a quick test to figure out how different Lisp implementations handle this.
;;; the test goes like this:
;;; let's grab two functions with the same body
;;; which uses the LOAD-TIME-VALUE trick
(defun test-function ()
(let ((counter-var (load-time-value (cons 0 nil))))
(symbol-macrolet ((counter (car counter-var)))
(incf counter))))
(defun test-function-2 ()
(let ((counter-var (load-time-value (cons 0 nil))))
(symbol-macrolet ((counter (car counter-var)))
(incf counter))))
;;; let's compile only the second one
;;; and leave the first one possibly uncompiled
(compile 'test-function-2)
;;; let's call each one a few times
(format t "Possibly not compiled code: ~D ~D ~D~%"
(test-function) (test-function) (test-function))
(format t "Compiled code: ~D ~D ~D~%"
(test-function-2) (test-function-2) (test-function-2))
The results divide the CL world pretty much in half, speaking numerically.
SBCL 2.1.11
CCL 1.12
ECL 21.2.1
Clasp current
ABCL 1.8.0:
CLISP 2.49.93+:
ACL 10.1 Express:
LW 7.1.2 Personal:
So, it's time to update the STATIC-LET
article I wrote yesterday and add a warning to it that it's only safe to use STATIC-LET
in compiled code, or on implementations which are compiler-only.
But alas! It's also possible to use this behavior to check if a piece of code has been minimally compiled. The following example signals an error only on the latter four implementations and only in code that has not been compiled.
;; I used the stones to destroy the stones
;; CLISP 2.49.93+
[1]> (defun foo ()
(flet ((fn () (let ((x (load-time-value (list 0)))) (incf (car x)))))
(declare (notinline fn))
(when (= (fn) (fn))
(error "STATIC-LET will not work in uncompiled code."))))
FOO
[2]> (foo)
*** - STATIC-LET will not work in uncompiled code.
The following restarts are available:
ABORT :R1 Abort main loop
Break 1 [3]> :r1
[4]> (compile 'foo)
FOO ;
NIL ;
NIL
[5]> (foo)
NIL
On non-compiler-only implementations, it's possible to splice such a piece of code into a macroexpansion in order to have a check which will signal an error (or possibly do something else) on code which was not minimally compiled.
Or, in other words, I just realized that (flet ((fn () (let ((x (load-time-value (list 0)))) (incf (car x))))) (= (fn) (fn)))
is a non-portable but luckily working poor man's "was this code minimally compiled" predicate.
I bet $3 that it's useless other than for the above.
via Elm - Latest posts by @rupert Rupert Smith on Sun, 30 Jan 2022 21:43:26 GMT
Just to be clear, I am definitely not promoting “components”. I also used Html.map
AND a (SmallComponentMsg -> msg)
together in my example, it was not a choice of one OR the other:
-- Passed in a (SmallComponentMsg -> msg) arg as toMsg.
someView : (Msg -> msg) -> Model -> Html msg
someView toMsg model =
Html.div
[ onClick DoStuff ]
[ ... ]
|> Html.map toMsg
-- Applied it with Html.map
via Elm - Latest posts by @Latty Gareth Latty on Sun, 30 Jan 2022 14:53:35 GMT
So Html.map and (SmallComponentMsg → msg) functions are basically the same thing.
The whole point of my post was that they aren’t. With Html.map
you are locked into only one “level” of message type. Taking functions lets you take different ones (e.g: one for wrapping internal messages, one for some external result), which is more easily flexible. You can of course emulate one with the other (your component can have a message type that has branches for internal/external and then have map calls disassemble that), but some ways of doing things push you into certain patterns and mindsets, picking the more natural ones can make things easier.
So I guess, my question becomes: why is it frowned upon in the Elm community to divide your development into components?
It isn’t, rather, it’s a bad idea to automatically split all of your code into components, as it adds complexity and forces you to write a lot of boilerplate for little gain. Making each page it’s own isolated component in a multi-page application, for example, is generally a bad idea. They are only going to be used in one place, and often end up needing to touch shared parts of the model.
If you have something that really makes sense as a component, something that needs to be done in many places across your application and is reasonably self-contained, then yes, make it into a component.
via Elm - Latest posts by @AvailableUsername on Sun, 30 Jan 2022 13:30:50 GMT
So I guess, my question becomes: why is it frowned upon in the Elm community to divide your development into components?
I mean, as of now, Elm is used to develop user interfaces. User interfaces are naturally made of components. Date pickers, schedules, tabbed panes, menus, road maps, grids, pages, music staffs, pdf viewers, toasts, chatboxes, graphs, are just a few examples that come to mind. Having an Elm module that implements a component is great for reuse. Why would it be a bad thing?
Say I have an application that allows you to produce and edit documents that are sequences of paragraphs, images and music staffs.
Wouldn’t it be stupid to insist on putting all the messages into one update function ?
Wouldn’t it be more reasonable to make
- a module that implements a music staff, that allows you to place a note on a staff when you click on it, change the time signature etc…
- an image holder module that allows you to resize an image by a drag, or other kind of behaviors
- a paragraph module that allows you to change its font size, font colors etc…
- And have the main application delegate these behaviors to the smaller components ?
I could then publish, say, the music staff component on package.elm-lang.org for other people to use, or improve. This is better for the Elm community than a monolithic approach, where the messages dedicated to my music staffs would be some subtypes of a Message type of some big application I’m the only person to use.
via Elm - Latest posts by @AvailableUsername on Sun, 30 Jan 2022 12:40:39 GMT
I agree with rupert, adding a (SmallComponentMsg -> msg)
argument to your views or using Html.map
is architecturally the same thing. It allows you to split your developments into components. Html.map
allows the same kind of flexibility you would get using (SmallComponentMsg -> msg)
functions. You’re not forced to pass a BigAppMsg
constructor to Html.map
, you can pass any function that produces a message to it. Or you could handle particular cases in the update
function of your main application (or bigger component).
So Html.map
and (SmallComponentMsg -> msg)
functions are basically the same thing. The nice thing that Html.map
allows is that, since you’re not forced to add a (SmallComponentMsg -> msg)
parameter to the view function of your component, it’s immediate to use that component as a top level application if you want (the view function has the good signature to be fed to Browser.document
, Browser.application
etc…).
via Elm - Latest posts by @unsoundscapes Andrey Kuzmin on Sun, 30 Jan 2022 10:31:09 GMT
@Lucas_Payr on WebGL performance, read the “Making the most of the GPU” section of the package docs webgl 1.1.3 that is also linked from the Mesh docs.
In the nutshell, WebGL API in Elm is not meant for creating meshes in the view on each frame, because each time such mesh would have to be uploaded to GPU, that is a rather slow operation. The mesh needs to be created and cached in the model or globally, then reused.
Given your use case, I don’t think the current WebGL API works well, because it is not suitable for manipulating individual pixels on the screen.
via Planet Lisp by on Sun, 30 Jan 2022 09:42:44 GMT
So I have been working on Common Lisp Recipes, on a recipe for using global static bindings via global-vars. I was wondering if there was anything for local bindings though, something like in C:
// Simple test in C
int test_function() {
static int counter = 0;
return counter++;
}
test_function(); // -> 0
test_function(); // -> 1
test_function(); // -> 2
And then, oh, I remembered. There was an article exploring the topic and showing a technique that had some pretty nice syntax.
;;; Simple test in Lisp
(defun test-function ()
(static-let ((counter 0))
(incf counter)))
(test-function) ; -> 1
(test-function) ; -> 2
(test-function) ; -> 3
Oh, and it should come in a LET*
flavor too!
;;; Simple sequential test in Lisp
(defun test-function-2 ()
(static-let* ((counter 0)
(big-counter (+ counter 100)))
(list (incf counter) (incf big-counter))))
(test-function-2) ; -> (1 101)
(test-function-2) ; -> (2 102)
(test-function-2) ; -> (3 103)
The only thing that was missing was a usable and somewhat tested implementation of that technique that I could link people to from the recipe. There wasn't one, though... So, d'oh, it needed to be written and uploaded somewhere.
Where? The library of Serapeum accepted the idea and I was able to come up with an implementation. It satisfied the maintainer who also provided a few fixes, all of which are implemented in the article.
But, that's the boring stuff. Come! Let us indulge in a little bit of literate programming and figure out how exactly that code works.
Read the full article on GitHub.
via Elm - Latest posts by @Lucas_Payr Lucas Payr on Sun, 30 Jan 2022 05:05:43 GMT
Quick update: I now believe that it has nothing to do with WebGL.
I’ve replaced the Dict with an Array → im now getting 14 Frames locally, 7 in Ellie.
Also i added a way to skip frames (renderEvery
on line 55). If this would be a WebGL issue, then the frames should not change when increasing renderEvery
. But in fact when doubling it, the fps drop by half.
Another observation: removing the laplace function gets me up to 60 fps in Ellie. Heres the laplace function:
convolution : List ( ( Int, Int ), Float )
convolution =
[ [ 0.05, 0.2, 0.05 ]
, [ 0.2, -1, 0.2 ]
, [ 0.05, 0.2, 0.05 ]
]
|> List.indexedMap
(\j list ->
list
|> List.indexedMap (\i factor -> ( ( i, j ), factor ))
)
|> List.concat
get : ( Int, Int ) -> Array (Array a) -> Maybe a
get ( i, j ) grid =
grid
|> Array.get j
|> Maybe.andThen (Array.get i)
laplace : (( Float, Float ) -> Float) -> ( Int, Int ) -> Array (Array ( Float, Float )) -> Float
laplace fun ( x, y ) dict =
convolution
|> List.map
(\( ( i, j ), factor ) ->
(dict
|> get ( x - 1 + i, y - 1 + j )
|> Maybe.map fun
|> Maybe.withDefault (fun ( 1, 0 ))
)
* factor
)
|> List.sum
For a given point it sums up the neighbors (using the weights defined in convolution). Every cell has two values, thats why i need to pass fun
along, the function is currently called twice per cell: once with fun = Tuple.first
and once with fun = Tuple.second
. My next experiment will be to get rid of fun
. This should theoretically double the fps.
via Elm - Latest posts by @Sidney Sidney Nemzer on Sat, 29 Jan 2022 21:23:00 GMT
The example you linked performs very well because it runs the Gray-Scott algorithm on the GPU, rather than the CPU. It uses a feature of WebGL where it renders to a texture. Elm doesn’t expose an API to do this so I don’t think it can be written with pure Elm at this point.
via Elm - Latest posts by @Lucas_Payr Lucas Payr on Sat, 29 Jan 2022 19:29:01 GMT
Hi there,
I wanted to implement the Reaction-Diffusion Algorithm and notices that I can’t get past 4 FPS. (JS implementations have typically a stable 60 FPS)
How it should look like after a couple of seconds:
So why is Elm/WebGL that slow? How can i improve the calculations?
For some context: I’m working on an Elm package for writing generative Art. The reaction-diffusion algorithm is an essential part of this package. If I find out that it is defacto impossible to pull off a fast Reaction-Diffusion Algorithm in Elm, I can throw my package in the bin.
via Elm - Latest posts by @rupert Rupert Smith on Sat, 29 Jan 2022 17:41:21 GMT
The real point I was trying to make is that it is not really Html.map
(or Cmd.map
) that is the problem, its trying to split up an Elm application into stateful components that is. You would use Html.map
(or Cmd.map
) to do that, so it can be a code smell, but its not a certainty.
via Elm - Latest posts by @hansv Hans verschooten on Sat, 29 Jan 2022 17:14:42 GMT
Hi Terezka,
this was exactly the info I needed, thanks for going above and beyond in helping me out.
Have an excellent weekend,
Hans
via Planet Lisp by on Sat, 29 Jan 2022 16:38:43 GMT
#CommonLisp #Lisp
I just released In Nomine - a utility for creating, accessing, and managing custom namespaces in Common Lisp.
It's a backwards incompatible fork of lisp-namespace with more customization and tests.
Grab it from GitHub.
In hindsight, it's a very good way of shaving yaks while working on CLR2. It's not the first nor surely the last yak shaven that way. I have a namespacing utility that has enough functionality for me to like it and - more importantly - enough testware for me to trust it.
Maybe I can eventually port some code back into lisp-namespace so existing users can benefit from it.
Anyway! The difference list:
NAMESPACE-LET
and NSLET
,DEFINE-NAMESPACE
option to automatically generate NAMESPACE-LET
-based binding macros,DEFINE-NAMESPACE
to customize behavior of generated namespaces,via Elm - Latest posts by @rupert Rupert Smith on Sat, 29 Jan 2022 11:47:04 GMT
I was thinking leave them available - not so much for issues on the core package code, but someone might have some other random issue they want to mention. I think GitHub lets you set some text to guide people when entering issues, so can put a statement there directing them to the official repo if its a code issue.
via Elm - Latest posts by @system system on Sat, 29 Jan 2022 09:56:01 GMT
This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
via Elm - Latest posts by @Janiczek Martin Janiczek on Sat, 29 Jan 2022 09:27:11 GMT
Hello everybody,
inspired by @kvothe I took a stab at implementing Wordle in Elm too. Timed myself and set the timelapse to music.
I feel like this could become a nice “kata” for trying out different data structures. Lists, Arrays, records, arrays of guaranteed length, etc. The video shows a simple List approach.
Enjoy!
via Elm - Latest posts by @aiko Aiko Mastboom on Sat, 29 Jan 2022 06:50:17 GMT
You’re question is actually touching on a very fundamental problem “halting problem”.
However in your case it is possible to help ensure the loop exits eventually. Things like timeouts, Max depth counters and checking for loops in the current planned path come to mind.
Another thing that comes to mind is to try and break down the complexity of the algorithm, so you can test smaller chunks of it ( possibly without the need of the loop/recursive behavior wrapping it)
Just some ideas.
via Elm - Latest posts by @rauxyo Rob on Sat, 29 Jan 2022 04:19:59 GMT
Ah, that is interesting. I didn’t realize you could turn off PRs.
Well, I think turning off issues would be good too so those live in the official repos.