Feed Aggregator Page 644
Rendered on Tue, 08 Sep 2020 11:33:18 GMT
Rendered on Tue, 08 Sep 2020 11:33:18 GMT
via Elm - Latest posts by @akoppela Andrey Koppel on Tue, 08 Sep 2020 06:35:12 GMT
Hi folks.
I’ve written a short summary of a migration process from AngularJS to Elm with tools and strategies.
Hopefully it can help and inspire teams who could possible have to go down the same route at any given time.
Thank you.
via Elm - Latest posts by @miniBill Leonardo Taglialegne on Mon, 07 Sep 2020 22:54:05 GMT
You have type-nerd-sniped me. I think it might be possible to do an elm-url-codec, but I’ll have to check the types closely
via Elm - Latest posts by @system system on Mon, 07 Sep 2020 18:28:04 GMT
This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
via Elm - Latest posts by @jfmengels Jeroen Engels on Mon, 07 Sep 2020 14:16:01 GMT
Elm Radio episode 12 is live!
We discuss the benefits that using @RyanNHG’s elm-spa framework bring to your codebase, and how to get started.
via Elm - Latest posts by @system system on Mon, 07 Sep 2020 13:37:53 GMT
This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
via Elm - Latest posts by @system system on Mon, 07 Sep 2020 06:03:02 GMT
This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
via Elm - Latest posts by @system system on Mon, 07 Sep 2020 02:04:31 GMT
This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
via Planet Lisp by on Sun, 06 Sep 2020 15:21:46 GMT
Did you hear the story of how one developer broke thousand JavaScript libraries by removing 11 lines of code from NPM?
Now we can to repeat this feat because we have our own left-pad
for Common Lisp! :)
This library brings only one function, which adds some spaces, to make a string of given length:
POFTHEDAY> (trivial-left-pad:left-pad "Foo" 16)
" Foo"
POFTHEDAY> (trivial-left-pad:left-pad "Bar" 16)
" Bar"
POFTHEDAY> (trivial-left-pad:left-pad "Blah" 16)
" Blah"
POFTHEDAY> (trivial-left-pad:left-pad "Minor" 16)
" Minor"
POFTHEDAY> (trivial-left-pad:left-pad "Hello world!" 16)
" Hello world!"
You also can specify a custom padding as a character or a string:
POFTHEDAY> (trivial-left-pad:left-pad "Hello world!" 16 ".")
"....Hello world!"
POFTHEDAY> (trivial-left-pad:left-pad "Hello world!" 16 #\.)
"....Hello world!"
POFTHEDAY> (trivial-left-pad:left-pad "Hello world!" 16 ".!")
".!.!Hello world!"
POFTHEDAY> (trivial-left-pad:left-pad "Hello world!" 16 "->")
"->->Hello world!"
Of cause, this library is useful only if you need padding from more than one character. In other cases it is much easier to use standard format function:
POFTHEDAY> (format nil "~16@A" "Hello world!")
" Hello world!"
POFTHEDAY> (format nil "~16,,,'_@A" "Hello world!")
"____Hello world!"
POFTHEDAY> (format nil "~16,,,'+@A" "Hello world!")
"++++Hello world!"
;; If you want to pass padding in runtime:
POFTHEDAY> (format nil "~v,,,v@A" 16 #\+ "Hello world!")
"++++Hello world!"
via Elm - Latest posts by @Philipp_Krueger Philipp Krüger on Sun, 06 Sep 2020 14:25:12 GMT
Incidentally, I did try to do just that only 2 days ago.
It would certainly be possible with an API similar to elm-codec, but coming up with an API that is similar to Url.Parser seemed very difficult.
Elm-codec is essentially similar to e.g. Json.Decoder. But Url.Parser works differently.
-- hypothetical syntax
infix <*> = D.andMap
decode : D.Decoder Person
decode =
(D.map Person <*> D.field "name" D.string) <*> D.field "age" D.int
route : Parser (Person -> a) a
route =
Parser.map Person (Parser.string </> Parser.int)
They both have a map
, but with very different types. Both have some form of combination operator, D.andMap and </>. But you can see how the brackets associate differently.
While Json.Decode works like a basic Applicative, Url.Parser seems to have some kind of continuation-style API. I haven’t seen such an API used like that. I’d love to see more examples of it.
And yeah. I couldn’t easily build an API that feels similar to Url.Parser but achieves something like what elm-codec improves over elm/json.
via Elm - Latest posts by @sebbes Sébastien Besnier on Sun, 06 Sep 2020 11:09:58 GMT
About the “extraction Route type”, my idea is even a bit more general: for a given type T
in a module M
, have a fuzzer generating value of this type using all the functions available in M
.
This way you can easily check invariants about T
and this “parsing issue” for routes is then a trivial sub-issue of that.
Generating unbounded types is the role of the fuzzers (there is even a complete “theory” about how to “shrink” values).
Warning: trying to have a “perfect solution” sometimes leads to nightmare. We don’t launch spatial rockets, we don’t need 100% safety. Roughly speaking, I’d say with elm we have “95% safety” (by comparison of “50% safety” in JS) which is quite enough for web sites.
Honestly, my hand-written fuzzers work fine, adding totally new a route is pretty a rare. However, modifying the url parameters is pretty frequent and is well caught! So, yes, sometimes I could have some url-related bug (ergo “5% unsafe”), but I don’t need to mess up with additional tool or abstraction.
via Elm - Latest posts by @rkb Robert K Bell on Sun, 06 Sep 2020 10:45:08 GMT
Ah, thanks. So, the same limitation affects elm-serialize:
semaphoreCodec : S.Codec Semaphore
semaphoreCodec =
S.custom
(\redEncoder yellowEncoder greenEncoder value ->
case value of
Red i s b ->
redEncoder i s b
Yellow f ->
yellowEncoder f
Green ->
greenEncoder
)
-- Note that removing a variant, inserting a variant before an existing one, or swapping two variants will prevent you from decoding any data you've previously encoded.
|> S.variant3 Red S.int S.string S.bool
|> S.variant1 Yellow S.float
|> S.variant0 Green
-- It's safe to add new variants here later though
|> S.finishCustom
In fact, even the relatively simple “codec-style Route” code I posted earler had a bug - routeParser
didn’t handle the User
and Task
routes… I wasn’t careful enough!
(aside: that we don’t have to be so careful with languages like Rust and Elm is a big part of their appeal; features that require “you have to be careful!” compel me to seek alternatives)
So it seems fundamental that within Elm, there’s no way to statically enforce the symmetry of encoder/decoder pairs. From outside Elm, there seems to be two options:
elm-syntax
gives the AST, but that may be too low-level… would we need to recreate a subset of the compiler?)Both approaches might need some way to limit unbounded types like Int
, String
(and types that wrap those)… how much could we do for the general case? How much would the user need to tell the generators about how to generate their own unbounded types?
via Elm - Latest posts by @system system on Sun, 06 Sep 2020 10:39:29 GMT
This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
via Elm - Latest posts by @sebbes Sébastien Besnier on Sun, 06 Sep 2020 10:10:43 GMT
I’d love to have this! But since I am a bit lazy, I didn’t write this lib, I wrote fuzzers by hand instead… My other dream would be a tool automagically writing those fuzzers!
via Elm - Latest posts by @rupert Rupert Smith on Sun, 06 Sep 2020 10:06:07 GMT
So the idea would be to write a new codec package for symmetrically parsing and building URLs?
via Elm - Latest posts by @sebbes Sébastien Besnier on Sun, 06 Sep 2020 09:43:03 GMT
Martin’s idea about Codecs is to safely build decoder/encoder from small composable blocks. If you build a Codec only using his lib, you’ll have the GUARANTEE the decoder/encoder are in sync (except maybe only for customTypes I think, where the definitions are separate ; so you only have to be very careful for those kind of codec)!
It is a bit stronger than keeping encoder/decoder “close enough” (you have to be very careful at each codec definition).
via Elm - Latest posts by @sebbes Sébastien Besnier on Sun, 06 Sep 2020 09:01:24 GMT
Ok cool @dta! Thanks.
There are no way to guarantee that a given value x
returned by an “elm-converted-to-js” function cannot be modified by JS code. So I’ll never try do allow things like:
// js file:
var someOpaqueElmValue = elmModule.f(42);
var result = elmModule.g(someOpaqueElmValue);
via Elm - Latest posts by @rkb Robert K Bell on Sun, 06 Sep 2020 02:04:23 GMT
Thanks sebbes! I had tried to go the Fuzzer route, but not being able to automatically catch and test new variants seemed like a deal-breaker.
I hadn’t considered the codec approach… keeping the encoder and decoder right next to each other should make it obvious when one’s been missed, and hopefully also when they’re not symmetric (one of them isn’t correct)!
I had a go at a sample codec-style Route:
module RouteCodec exposing (..)
import Url exposing (Url)
import Url.Parser as UP exposing ((</>), Parser, s)
type Route
= Home
| Settings
| User String UserRoute
| Task Int TaskRoute
type UserRoute
= Summary
| Activity
type TaskRoute
= Active Bool
| Overdue
type alias Codec a b c =
{ encode : c -> String
, decode : Parser (a -> b) b
}
routeToString : Route -> String
routeToString route =
case route of
Home ->
routeCodecs.home.encode ()
Settings ->
routeCodecs.settings.encode ()
User username userRoute ->
routeCodecs.user.encode ( username, userRoute )
Task id taskRoute ->
routeCodecs.task.encode ( id, taskRoute )
routeFromString : String -> Maybe Route
routeFromString string =
string
|> (++) "https://x.y/"
|> Url.fromString
|> Maybe.andThen (UP.parse routeParser)
routeParser : Parser (Route -> a) a
routeParser =
UP.oneOf
[ routeCodecs.home.decode
, routeCodecs.settings.decode
]
routeCodec : Codec Route a Route
routeCodec =
{ encode = routeToString
, decode = routeParser
}
routeCodecs =
let
home : Codec Route a ()
home =
{ encode = always "home"
, decode = s "home" |> UP.map Home
}
settings : Codec Route a ()
settings =
{ encode = always "settings"
, decode = s "settings" |> UP.map Home
}
user : Codec Route a ( String, UserRoute )
user =
{ encode =
\( username, route ) ->
"user/" ++ username ++ "/" ++ userRouteCodec.encode route
, decode =
(s "user" </> UP.string </> userRouteCodec.decode)
|> UP.map User
}
task : Codec Route a ( Int, TaskRoute )
task =
{ encode =
\( taskId, route ) ->
("task/" ++ String.fromInt taskId ++ "/")
++ taskRouteCodec.encode route
, decode =
(s "task" </> UP.int </> taskRouteCodec.decode)
|> UP.map Task
}
in
{ home = home
, settings = settings
, user = user
, task = task
}
-------------------------------------------------------------- ↓ UserRoutes.elm
userRouteCodec : Codec UserRoute a UserRoute
userRouteCodec =
{ encode = userRouteToString
, decode = userRouteParser
}
userRouteToString : UserRoute -> String
userRouteToString route =
case route of
Summary ->
userRouteCodecs.summary.encode ()
Activity ->
userRouteCodecs.activity.encode ()
userRouteParser : Parser (UserRoute -> a) a
userRouteParser =
UP.oneOf
[ userRouteCodecs.summary.decode
, userRouteCodecs.activity.decode
]
userRouteCodecs =
let
summary : Codec UserRoute a ()
summary =
{ encode = always "summary"
, decode = s "summary" |> UP.map Summary
}
activity : Codec UserRoute a ()
activity =
{ encode = always "activity"
, decode = s "activity" |> UP.map Summary
}
in
{ summary = summary
, activity = activity
}
-------------------------------------------------------------- ↓ TaskRoutes.elm
taskRouteCodec : Codec TaskRoute a TaskRoute
taskRouteCodec =
{ encode = taskRouteToString
, decode = taskRouteParser
}
taskRouteToString : TaskRoute -> String
taskRouteToString route =
case route of
Active bool ->
taskRouteCodecs.active.encode bool
Overdue ->
taskRouteCodecs.overdue.encode ()
taskRouteParser : Parser (TaskRoute -> a) a
taskRouteParser =
UP.oneOf
[ taskRouteCodecs.active.decode
, taskRouteCodecs.overdue.decode
]
taskRouteCodecs =
let
active : Codec TaskRoute a Bool
active =
{ encode =
\bool ->
if bool then
"active"
else
"inactive"
, decode =
UP.oneOf
[ s "active" |> UP.map (Active True)
, s "inactive" |> UP.map (Active False)
]
}
overdue : Codec TaskRoute a ()
overdue =
{ encode = always "overdue"
, decode = s "overdue" |> UP.map Overdue
}
in
{ active = active
, overdue = overdue
}
(gist version here, might be easier to read)
I may have missed something fundamental about Codecs, though… does a record with encode
and decode
properties meet the definition, or should it be constructed some other way?
via Elm - Latest posts by @dta David Andrews on Sat, 05 Sep 2020 20:17:35 GMT
It would not be possible for another piece of code to modify arg
during the execution of elmModule.f
since JavaScript is single-threaded. However, if elm code keeps a reference to the json Value
, it is possible for two decodings of the value to produce different results if some non-elm code runs in between.
In a normal elm program, this is a possibility (and is actually sometimes useful), but I don’t know enough about your code to know whether it is a possibility in your library. Does your library ever keep any state, or in other words does your library allow wrapping Program
types? If not, then it should be safe.
via Planet Lisp by on Sat, 05 Sep 2020 19:01:19 GMT
Another month already. Fortunately there's a lot of stuff to talk about for Kandria this time around, so I hope you're ready for a beefy summary!
This month was originally intended to be focused on marketing and recruitment, but for more reasons than one that's not entirely how it went down after all. The biggest reason among the bunch being that it has proven really hard for me to concentrate on that - they're topics I have no experience with, so I don't feel very comfortable dealing with them at all. Regardless, there's been a few things I've done to help in that regard:
There is now a lengthy game design document that should hopefully give a good idea of what the game should be and should be about. It's mostly meant as a communication tool for future team members, to help them get up to speed on the project, and for potential team members to evaluate whether this project is something for them. If you have a read through it, I would appreciate your thoughts on it a lot! Being in an opinion vacuum doesn't help with creativity.
Next I started working on an official job listing. It's not quite done yet and I want to run it by a few people before I get it out there, but if you are or know pixel artists or writers, I'd appreciate it tremendously if you could keep an eye open for the listing. I'll make another announcement about it on all the channels once it's out.
I've also started a thread on some forums to try and spread awareness of the game, but I'm worrying that I'm spreading myself too thin and can't tend to all of the outlets as much as I'd like. There's already email, Twitter, and Discord to take care of besides that. With Kandria not being a full-time job yet, I'm not sure how much time I should be spending on the social media channels, rather than spending it on development. Maybe I should reserve a day for it every week? Either way, top priority for the coming few weeks will be finding new team members, and I'll definitely have to invest some more energy to get that going.
I've also made some good progress on the code and art side: first I've implemented some more dynamic interactions to make the environment feel more alive:
These, especially the under water physics, still need some work in the future, but for only having spent a single day on them they already look quite promising. I have a few more ideas for dynamic interactions like that that I want to try and implement soon, too. All in all this should really help to make the world feel more alive and real, rather than being just a static map of tiles.
Sort of adjacent to this I created a custom distortion effect that'll be useful for indicating damage and death:
Then there were mechanical changes to the game: so far you could climb up walls indefinitely. I've decided to change this and implement a simple stamina system, as this will allow greater control over where the player can or cannot go, thus making it possible to block off certain routes and regions until later. It should also provide for more interesting platforming challenges and interactions with other elements like the rope.
Finally there were a bunch of good bugfixes thanks to public feedback! These aren't rolled out in the latest downloadable prototype yet, but they'll be in the next one, which I hope to release sometime this month. I've also automated deployment almost fully, allowing me to upload new updates at the push of a button. For now I'm keeping the public prototypes at a staggered release schedule, with a separate rolling release tester group on Steam. I'll provide more info about the Steam testing group once I have a better bug reporting system in place.
Then I've gotten back to re-integrating the dialog system. This is now pretty much done, the only missing component is the quest system that controls what dialog can be active at what point and things like that. I'll probably get to that next once I've rounded up some more issues with audio, though more on that in a second. The dialog system I have in Kandria is pretty powerful, and I've written a lengthy bit about it in the weekly newsletter. I also started working on profile animations for that:
They'll need some more work though, as I'm not convinced they fit very well into the dialog box as I've got it so far.
I think I'll have to try out anti-aliasing to smoothen the animations out some more. Maybe that'll make it feel more at home with the pretty crisp textbox. That's not very high on my list of priorities though, so I'll keep it for another time.
Finally, after months of pretty painful debugging and coding, I've made a breakthrough with my audio engine! It's now conceptually complete and just needs some good integration testing within Kandria. The good news is that, aside from a missing reverb implementation, it should offer everything I need for Kandria, and more, so using it for all of my future projects is definitely on the table. Since this has been such a long and arduous journey, I wanted to take some good time to explain the system. If you're interested in that, you can read about it here. Suffice to say, the next prototype release will finally have sound!
It'll be a bit before the sound in Kandria will be custom and accompanied by nice, composed music specifically for the game, though. Music and sound is something I've been thinking about for a long time, but I'm purposefully putting it off for much later, as I know it needs to fit the places, story, and characters, all of which have not been sufficiently worked out yet.
Looking back at the roadmap that I published in June, it seems like I'm getting ahead pretty well despite the many issues that propped up along the way. A lot of the big blockers have already been fixed, and with a bigger team the rest should get ahead pretty well, too. I don't want to jinx it, but it's looking like the March deadline for the vertical slice is doable!
Alright, so to summarise the plan for this month: complete the job listing, re-integrate the quest system, add some preliminary sounds and music, improve the bug reporting, and release the 0.0.3 demo. We'll see how much of that, if not more, I get done by next month.
If you want to get the weekly updates with more details on what's going on until then, subscribe to the newsletter!
via Planet Lisp by on Sat, 05 Sep 2020 19:00:45 GMT
This system is a compatibility layer for defining global variables. Global variables cannot be dynamically bound and sometimes is faster than variables defined with defvar
or defparameter
.
Also, when you are using globals-vars
to define a variable, you tell the user of the library that this variable is not intended to by dynamically bound.
Here is a test for speed, comparing access to standard variable and global variable:
POFTHEDAY> (declaim (optimize (debug 1) (speed 3)))
POFTHEDAY> (defvar *global* 0)
*GLOBAL*
POFTHEDAY> (global-vars:define-global-var -global- 0)
-GLOBAL-
POFTHEDAY> (time (loop repeat 1000000000
do (incf *global*)))
Evaluation took:
2.339 seconds of real time
2.339325 seconds of total run time (2.336514 user, 0.002811 system)
100.00% CPU
5,164,301,132 processor cycles
0 bytes consed
POFTHEDAY> (time (loop repeat 1000000000
do (incf -global-)))
Evaluation took:
1.560 seconds of real time
1.560328 seconds of total run time (1.558862 user, 0.001466 system)
100.00% CPU
3,444,078,626 processor cycles
0 bytes consed
As you can see, accessing of variable, defined as global
is almost twice faster on SBCL.
There is also macros define-global-var*
and define-global-parameter*
. They will define variables which will not be available in compile-time. Why does somebody might want this?