<FromGitter>
<watzon> @j8r I linked it. There's one called hypospray
<FromGitter>
<watzon> Don't know how actively developed it is though
<FromGitter>
<watzon> Not super actively apparently. Last commit was in 2017
blassin has joined #crystal-lang
<FromGitter>
<j8r> Yes I said because of the last commit :/
<FromGitter>
<j8r> sounds even harder to do it in Rust than Crystal
absolutejam has quit [Ping timeout: 245 seconds]
jolav has joined #crystal-lang
<absolutejam1>
So, I know that I need to wrap union-returning methods in a local var
<absolutejam1>
so `myvar = foo.bar` where `foo.bar : String?`
<absolutejam1>
but is there an easy way to provide the guard clauses for the compiler, besides loads of if/unless statements?
<absolutejam1>
For example, I run a method to ensure that `foo.bar` is not nil as an earlier part of my application, raising an exception if it is (although, there's a default, so it can never be nil in this case)
<FromGitter>
<j8r> absolutejam1 typing arguments?
<FromGitter>
<j8r> and return types
<absolutejam1>
I guess, but because this is coming from the CLI, it kinda has to be a union in this case
<absolutejam1>
maybe I could add a helper function so instead of `foo.bar` I'm calling `foo.get("bar", :string)`
<FromGitter>
<j8r> there error will be more helpful than `#not_nil!`
<FromGitter>
<j8r> the best would be a custom error, but it may be verbose at times
<absolutejam1>
`as` is helpful, thanks
jolav has quit [Remote host closed the connection]
<absolutejam1>
I'm just trying to stop nesting so deep when I have already made the appropriate checks elsewhere
<absolutejam1>
and this isn't going to have any mutation in fibers etc
<FromGitter>
<j8r> have you assigned to a local var?
<absolutejam1>
yeah
<absolutejam1>
I think I've got it now with your help
<absolutejam1>
like I say, I had it working but I was looking to stop endless nesting of guard clauses
<absolutejam1>
Just cut out so much repitition in my code
<FromGitter>
<j8r> π
olbat[m] has quit [Ping timeout: 252 seconds]
dwdv has quit [Ping timeout: 276 seconds]
gangstacat has joined #crystal-lang
Seich has quit [Quit: Gone fishing.]
Seich has joined #crystal-lang
olbat[m] has joined #crystal-lang
duane has quit [Ping timeout: 245 seconds]
duane has joined #crystal-lang
<FromGitter>
<bararchy> So, what do you think about Fiber priority ? something like `spawn(priority: 1) do` which tells the schedular to make sure this Fiber is always get's checked for before others
<FromGitter>
<andrius> Question: Would this model work? Do I need to care about GC or so on? I am going to send quite a lot of events (question about `class Event`): https://play.crystal-lang.org/#/r/7a7s β β The app listen on TCP socket and receive key-value data; based on some keys and subscribers/events, those should be addressed for further processing
<FromGitter>
<j8r> If you don't need to pass it as a reference, you can use structs
sagax has quit [Ping timeout: 244 seconds]
<FromGitter>
<j8r> `Channel` and `Hash` are classes, it would be fine
sagax has joined #crystal-lang
<FromGitter>
<andrius> Hm, but what about memory? Shall I take care about Channel closure and cancel of future even if I sent to the channel and future became completed?
<FromGitter>
<j8r> I'am not familiar with channels intrinsic
<FromGitter>
<j8r> what I know is struct put much less pressure than classes on the GC
Raimondi has quit [Quit: WeeChat 2.5: Β‘Chau!]
lucasb has joined #crystal-lang
Raimondi has joined #crystal-lang
<jokke>
any chance maiha @maiha is here?
laaron has quit [Remote host closed the connection]
absolutejam1 has quit [Quit: WeeChat 2.5]
<FromGitter>
<girng> @absolutejam_gitlab a good way to prevent nesting that i found are guard clauses, helps a lot
<FromGitter>
<girng> because you know.. f*ck local variables and scope. let's put a variable in a guard clause and now it works for that entire block!
<FromGitter>
<asterite> @andrius I don't think there's any problem. You can try exercising the code a lot and see if it leaks memory... I don't understand why it would leak. Also, you can probably replace `future { ... }` with `spawn { ... }` (plus I'm pretty sure we'll remove `future` at some point)
<FromGitter>
<girng> @asterite "endless nesting of guard clauses", wait what. can i c an example of this lol
<FromGitter>
<girng> shouldn't the guard clauses just be sequential right after each other?
<FromGitter>
<girng> since the one before it must be true for the next clause to work..
<FromGitter>
<girng> @absolutejam_gitlab meant to tag, not asterite. my bad
<FromGitter>
<girng> now that i think of it. let's go a bit deeper.. is it possible to create guard clause chaining?
<FromGitter>
<girng> actually, can't you just do one `return unless (condition) && (condition)` right?
<FromGitter>
<girng> basically chain them, with &&s, lol
<FromGitter>
<girng> just tested it (https://play.crystal-lang.org/#/r/7a8t), yep it works. guard chaining is already possible , goes from left to right in sequential order
<FromGitter>
<girng> that's really cool, because the first local variable assignment can then be used inside all the adjacent conditionals. β so instead of β β ```return unless (client= players[200]?) β return unless client.in_game_id == 1234``` β ... [https://gitter.im/crystal-lang/crystal?at=5d387895d1cceb1a8db43899]
<FromGitter>
<j8r> but you assign a var inside a conditional
<FromGitter>
<girng> and?
<FromGitter>
<tenebrousedge> that's usually worth considering how to avoid
<FromGitter>
<girng> why
<FromGitter>
<girng> would you want to avoid that, rofl
<FromGitter>
<j8r> or alternatively `players[200]?.try { |cient| client.in_game_id.==(1234) }`
<FromGitter>
<j8r> because in Ruby/Crystal we can usually avoid parentheses, unlike Python and other languages
<FromGitter>
<girng> so instead of making the code easy to read, you want to use ?.try and &.== syntax? that's far from being "easier to read" than a simple expression inside parentheses
<FromGitter>
<Daniel-Worrall> What is your alternative that's easier to read?
<FromGitter>
<girng> my code?
<FromGitter>
<girng> it's literally 2 expressions
<FromGitter>
<Daniel-Worrall> Yes, how would *you* represent it?
<FromGitter>
<girng> did you not even see my code
<FromGitter>
<j8r> chaining method just sound OOP, and natural IMO
<FromGitter>
<girng> @j8r TIL parentheses make code hard to read
<FromGitter>
<j8r> not necessarily, @tenebrousedge just give a more expressive code
<FromGitter>
<Daniel-Worrall> ah
<FromGitter>
<Daniel-Worrall> yeah, I much prefer the chain
<FromGitter>
<asterite> I usually prefer one line for each condition that bails, optionally with a comment explaining why we bail
<FromGitter>
<girng> you can chain conditionals without relying on &.==(int) `shenanigans` that look cumbersome. or, you can just do.. client.in_game_id == 123 which is way more expressive, and easier to read
<FromGitter>
<j8r> I may agree, which can be magic for non initiated Crystalers unfamiliar with `&.`
<FromGitter>
<j8r> expressiveness is less code, right?
<FromGitter>
<tenebrousedge> I don't like writing code that assumes that the reader is unfamiliar with the language it's written in, but that's a personal preference
<FromGitter>
<girng> I don't like writing code that assumes that the reader is familiar with the language it's written in, and then needs to use more brain power to read the syntax to understand what it's doing
<FromGitter>
<j8r> We are going to the personal preference, syntax is a matter of taste
<FromGitter>
<girng> `return unless (client = players[200]?) && client.in_game_id == 1234` β vs β `return unless players[200]?.try &.in_game_id.==(1234)` β β @tenebrousedge `client` needs to be assigned as well. your example is not the same as my code [https://gitter.im/crystal-lang/crystal?at=5d388725d6188741642dd6e9]
<FromGitter>
<tenebrousedge> I would probably want a `find_client` method then
<FromGitter>
<girng> or.. use my example
<FromGitter>
<girng> where it assigns it to the obj, based on the conditionals
<FromGitter>
<girng> why is this such a weird thing to do?
<FromGitter>
<girng> it's so damn simple i love it, but you guys think it's the wrong way, why
<FromGitter>
<tenebrousedge> I feel like you're going to look back at your code in a few years and think very differently about many things
<FromGitter>
<tenebrousedge> what is `200` in the above code?
<FromGitter>
<girng> just random id for the contrived example
<FromGitter>
<tenebrousedge> why isn't it the same as the `in_game_id`?
<FromGitter>
<girng> because it's the user's id
<FromGitter>
<tenebrousedge> why have two different `id`s for the same record?
<FromGitter>
<girng> because this is a contrived example
<FromGitter>
<girng> just to show 2 guard clauses in action
<FromGitter>
<tenebrousedge> okay
<FromGitter>
<girng> @tenebrousedge i hope when you look back at your code in a few years you'll think the same as well
<FromGitter>
<girng> yeah i had 2 guard clauses like that, then figured i could write them in 1 conditional. i actually used to do differently where the blocks were nested 2-3 times
<FromGitter>
<girng> @Blacksmoke16 fixed it for me to us guard clauses
<FromGitter>
<girng> i don't know if more guard clauses acan be expressive, or convoluted. but i'feel comfortable using around 2 or 3
<FromGitter>
<girng> it still blows my mind how a guard clause, with an assigned local variable inside of its condition, can be used for the rest of the block
<FromGitter>
<girng> i always thought, local variables inside a condition are bound to that block, since they are a local variable
<FromGitter>
<tenebrousedge> conditionals do not create a new scope
<FromGitter>
<girng> > conditionals do not create a new scope β β In my example, the conditional in the guard clause, now makes `client` available to the entire `my_test` method
<FromGitter>
<tenebrousedge> if conditionals created a new scope, then any variables created within that scope would not be available afterwards
<FromGitter>
<girng> So yes, they do create a new scope
<FromGitter>
<tenebrousedge> what do you think "scope" is in this context?
<FromGitter>
<girng> variable access for `my_test`?
<FromGitter>
<tenebrousedge> methods do create a new scope
<FromGitter>
<tenebrousedge> so a simpler example would be the guard clauses discussed previously
<FromGitter>
<tenebrousedge> where as you point out, variables created within that scope are subsequently available outside the conditional
<FromGitter>
<girng> if you assign a variable in a guard clause, the variable now becomes available for the scope of the method right?
<FromGitter>
<tenebrousedge> correct
<FromGitter>
<girng> so is it a "new scope", or is it just modifying the original scope of the method, allowing it to be used
<FromGitter>
<girng> it's the latter, probably. but to me, it seems like it's the former
<FromGitter>
<girng> hard to explain
<FromGitter>
<girng> > where as you point out, variables created within that scope are subsequently available outside the conditional β β Yeah, problem is, if there is a variable that is ref to a class, and it has properties, that variable in essence really is only bound to that conditional
<FromGitter>
<girng> @tenebrousedge you are correct then. however i believe i'm partially correct in terms of if that variable is a reference to a class with properties
absolutejam has joined #crystal-lang
absolutejam has quit [Ping timeout: 244 seconds]
<FromGitter>
<asterite> there are no scopes in Ruby/Crystal inside methods. Well, the only thing that creates a scope is a block
<FromGitter>
<asterite> even if you do `begin ... rescue => ex end` in Ruby, `ex` will be available afterwards
<FromGitter>
<andrius> is there any way to update time format in logger? crystal is too fast :)
<FromGitter>
<absolutejam_gitlab> but I did also notice that β β ```unless (a = "foo") && (b = "bar") β raise Exception.new("aawfwaf") β end β β puts a``` β β works as I've used similar in guard clauses [https://gitter.im/crystal-lang/crystal?at=5d38a96cbe916e76e22dd3e1]
<FromGitter>
<watzon> The nice thing is, the compiler has your back
<FromGitter>
<absolutejam_gitlab> although I don't generally tend to do that
<FromGitter>
<absolutejam_gitlab> And I've been trying to use `#reduce` more since that's kinda what you have to use in Elixir and I like the functional style
<FromGitter>
<watzon> I love `reduce`. Once you understand it, it becomes extremely powerful
<FromGitter>
<tenebrousedge> I'm pretty sure that `reduce` can be used to write every other higher-order iterator
<FromGitter>
<absolutejam_gitlab> It took me *so* long to understand it, coming from the imperative style of a loop that mutates an outer variable
<FromGitter>
<absolutejam_gitlab> but I like the atomic nature of it
<FromGitter>
<absolutejam_gitlab> And, like I say, it's kinda the only real option for that kinda thing in Elixir outside of recursive functions (and it's the same process essentially)
<FromGitter>
<absolutejam_gitlab> I absolutely love `.map` and `.each` syntax though
olbat has quit [Quit: Bye ...]
<FromGitter>
<absolutejam_gitlab> And my first passion was PowerShell, so this resonates with me.
<FromGitter>
<Blacksmoke16> well for that you could just do `.sum`
<FromGitter>
<absolutejam_gitlab> the problem is, a lot of the examples have an alternative convenience method
<FromGitter>
<girng> like.. why not just have your method look like this: β β ```code paste, see link``` β β why do developers think hiding functionality in "short syntax" is a good thing? [https://gitter.im/crystal-lang/crystal?at=5d38ae5eec5abe7bbc40802a]
<FromGitter>
<absolutejam_gitlab> Because you're mutating `sum`
<FromGitter>
<tenebrousedge> ^^
<FromGitter>
<absolutejam_gitlab> A `reduce` call has no (well, should have no) side effects
<FromGitter>
<girng> that makes it even more confusing. so sum modified self?
<FromGitter>
<absolutejam_gitlab> If I explain it like in Elixir, you can't access variables outside of your scope
<FromGitter>
<absolutejam_gitlab> so you couldn't access `sum` inside the `each` block
<FromGitter>
<absolutejam_gitlab> The `each` example is mutating `sum` in every iteration
<FromGitter>
<girng> why even have a method that modifies self without it being explicit, for example, using `!`
<FromGitter>
<absolutejam_gitlab> but in a `reduce` statement, you're essentially instantiating a new variable and passing it to the next iteration each time
<FromGitter>
<absolutejam_gitlab> I'm lost
<FromGitter>
<tenebrousedge> it doesn't modify `self`, it modifies the `sum` variable from inside the loop
<FromGitter>
<tenebrousedge> reaching outside the loop to modify something is an anti-pattern
<FromGitter>
<girng> why is mutating it bad though
<FromGitter>
<girng> the developer is still getting the sum variable in the end
<FromGitter>
<absolutejam_gitlab> in that example, it's minor. But what if you're mutating a property of a class that is accessed across multiple fibers?
<FromGitter>
<tenebrousedge> in this case, it's not terrible, but if you were modifying `@sum` you would be asking for trouble
<FromGitter>
<tenebrousedge> it makes testing harder for one thing
<FromGitter>
<girng> oh, oops i thought james said modifies the array not the sum variable. i read that wrong. that's why i talked about `!`
<FromGitter>
<girng> but still, i think it's less expressive, no ?
<FromGitter>
<tenebrousedge> it's more expressive, less explicit
<FromGitter>
<girng> LOL
<FromGitter>
<girng> how reduce more expressive than a sum method
<FromGitter>
<girng> the sum method literally shows what the code is doing
<FromGitter>
<tenebrousedge> we're comparing your `sum` vs a `reduce`
<FromGitter>
<absolutejam_gitlab> Yeah, like I said, reduce is usually less useful when there are convenience methods available
<FromGitter>
<tenebrousedge> it's the second-lowest-level higher-order iterator
<FromGitter>
<tenebrousedge> the lowest is `each`
<FromGitter>
<tenebrousedge> we should prefer the highest level of abstraction available
<FromGitter>
<girng> in this example (https://play.crystal-lang.org/#/r/7abv/edit), `sum2` looks way more expressive β `.reduce` just seems like short-hand syntax. short hand syntax by definition cannot be more expressive than non-short-hand syntax
<FromGitter>
<tenebrousedge> shorter == more expressive
<FromGitter>
<tenebrousedge> seems like that could be a `map`?
<FromGitter>
<absolutejam_gitlab> hm, maybe
<FromGitter>
<absolutejam_gitlab> that definitely should be, haha
<FromGitter>
<tenebrousedge> I have only one example, testing whether the brackets in a string are evenly matched, and it's something of an abuse
<FromGitter>
<absolutejam_gitlab> I feel like I definitely had a couple but I dunno where they went. One of them I turned into a recursive function but I can't find any more
<FromGitter>
<absolutejam_gitlab> @tenebrousedge true, it's just a superficial thing
<FromGitter>
<absolutejam_gitlab> mutability isn't evil, despite what the functionalists will say
<FromGitter>
<absolutejam_gitlab> but I do prefer a more functional style, but it can be more cumbersome in an environment that doesn't force it (eg. Crystal/Ruby). But the tradeoffs are *usually* more predictability due to less side effects, and if you follow the principle of single responsibility, you get other benefits (testability, etc.)
<FromGitter>
<absolutejam_gitlab> but in the end, do what works best for you
<FromGitter>
<absolutejam_gitlab> In Crystal, does `a = foo.map { |x| ...whatever... }` mutate foo?
<FromGitter>
<tenebrousedge> circular dependencies would seem to be the developer's problem
<FromGitter>
<Blacksmoke16> sure but if its possible to catch them at compile time that would be preferable
<FromGitter>
<Blacksmoke16> nice to have, not a requirement id say
<FromGitter>
<Blacksmoke16> idk how the logic for that would work atm yet in macro land
<FromGitter>
<tenebrousedge> there are so many things that are easier in Ruby than in Crystal
<FromGitter>
<Blacksmoke16> i could believe it
<FromGitter>
<tenebrousedge> relevant to this problem, as an interpreted language, you have all other methods available during metaprogramming
<FromGitter>
<Blacksmoke16> yea, having more macro methods + reflection would prob make this a lot easier
<FromGitter>
<tenebrousedge> the downside of course being speed and type safety
<FromGitter>
<tenebrousedge> and it's hard to make the argument that Crystal *should* have arbitrary methods available in macros
<FromGitter>
<watzon> I've said this before and I'll say it again, although I know it won't make a difference. I would love for Crystal to have a macro system on par with Nim.
<FromGitter>
<watzon> In Nim you have full access to the AST
<FromGitter>
<watzon> Means you can do some very powerful things
<FromGitter>
<girng> > circular dependencies would seem to be the developer's problem β β lol last time i said something was the developer's problem, i got shunned