ChanServ changed the topic of #crystal-lang to: The Crystal programming language | https://crystal-lang.org | Fund Crystal's development: https://crystal-lang.org/sponsors | GH: https://github.com/crystal-lang/crystal | Docs: https://crystal-lang.org/docs | Gitter: https://gitter.im/crystal-lang/crystal
postmodern has joined #crystal-lang
<postmodern> here's a weird question i ran into while porting some ruby code to crystal, how does one do an empty named tuple? Trying to port some spec code where a keyword is given an empty options Hash.
<FromGitter> <Blacksmoke16> i think you can just do `NamedTuple.new`
<FromGitter> <Blacksmoke16> but ofc, another option is to not use named tuples like that :S
deavmi has joined #crystal-lang
<postmodern> yeah... what was originally a clever idea of "oh I'll just keyword-splat that Hash of Symbols" now has me reconsidering things as I'm porting this code to crystal
<FromGitter> <Blacksmoke16> using it as a container of named args is its purpose, but when you get into using it to model data it's not longer ideal
<FromGitter> <Blacksmoke16> named args with default values would prob be easier/more clear
ur5us has joined #crystal-lang
Chillfox has joined #crystal-lang
<postmodern> i really wish you could write type signatures that contained literal values (ex: `Int32 | '*'`)
<FromGitter> <Blacksmoke16> maybe could use an enum for that?
<FromGitter> <Blacksmoke16> wouldn't be the greatest UX tho needing to do like `MyEnum.new(14)` tho :/
Chillfox has quit [Ping timeout: 260 seconds]
Volk has quit [Quit: See you next time!]
Chillfox has joined #crystal-lang
<FromGitter> <Blacksmoke16> i wonder if that's something you could implement with like ameba
<postmodern> my edge-case is hexdump code which omitts repeating rows by yielding '*' instead of the incremental row index, so i have to say Int32 | String
<FromGitter> <Blacksmoke16> do you have to yield at all?
<FromGitter> <Blacksmoke16> probably?
<postmodern> there are other cases where i could use an enum, like the numeric base (2, 8, 10, or 16), but it's annoying to have to type Base::BINARY instead of just 2
<postmodern> yes because i need to still print the single line with the '*' in it
<FromGitter> <Blacksmoke16> if the arg is typed as the enum, could just do `:binary`
<postmodern> similar to `hexdump -C` output
<postmodern> ah i didn't know symbols could automatically be coerced to enum values?
<FromGitter> <Blacksmoke16> under certain cases yea
<postmodern> i think i'll make a shard to add NL or EOL constant. shame you can't define a macro with an uppercase name to mimic a constant
<FromGitter> <Blacksmoke16> just make it a constant in the first place?
<postmodern> will the crystal compiler optimize out the constant, or will it fetch the value from memory each time?
<FromGitter> <Blacksmoke16> im not sure, I'm sure LLVM would handle it well tho
<postmodern> preferance onf whether the NL or EOL constant is top-level or defined in String ?
<FromGitter> <Blacksmoke16> https://godbolt.org/z/5x8vbz173 idk if im doing this right but looks like both methods resolve to the same LLVM IR
<FromGitter> <Blacksmoke16> imo i'd go top level `EOL`
Liothen has quit [Ping timeout: 245 seconds]
<FromGitter> <Blacksmoke16> er i think that's technically asm?
Liothen has joined #crystal-lang
<postmodern> i also wish crystal had syntactic sugar for .not_nil!
<FromGitter> <Blacksmoke16> it does, just handle it properly :P
<postmodern> esp when using multiple not_nil! in a method chain: foo.not_nil!.bar.not_nil!.baz
<FromGitter> <Blacksmoke16> do all of those *need* to be nilable?
<postmodern> yes, unfortunately, because it's a drill down API where you can initialize things at a fine-grained level
MiaChillfox has joined #crystal-lang
<FromGitter> <Blacksmoke16> fair enough
<postmodern> grumble, the compiler is claiming an ivar is not initialized in #initialize, likely because it's set after yielding self for configuration purposes
<FromGitter> <Blacksmoke16> sounds likely
Chillfox has quit [Ping timeout: 252 seconds]
<postmodern> shards don't have some sort of auto-require file do they that get's automatically required in by the compiler?
<FromGitter> <Blacksmoke16> no
<postmodern> what's the canonical dir-structure for adding things to the top-level, especially things designed to extend crystal? `src/core_ext`?
<postmodern> or will `src/eol.cr` be sufficient?
<FromGitter> <Blacksmoke16> `ext/` directory is pretty common
<FromGitter> <Blacksmoke16> could also just put it at the top level of your main entrypoint file, given it's just literally a const that doesn't really extend anything
<postmodern> preference on shard name? crystal-eol or eol.cr?
<FromGitter> <Blacksmoke16> wouldn't it be easier to just make a PR to add it and resolve that issue
<FromGitter> <Blacksmoke16> it's trivial enough to just copy paste the like 5 lines that shard would be until it's released
<postmodern> ah sure, i'd have to familiarize myself with how to write the tests
<postmodern> also whats the ETA on 1.1.2?
<postmodern> if that's going to take a while to release, i might just use a shard in the mean time
<FromGitter> <Blacksmoke16> i dont think there would really be anything to test
<FromGitter> <Blacksmoke16> 1) 2 is being released soonish, but you missed the merge window, so wouldn't be till 1.3
<postmodern> well i'd probably want to check that under {% if flag?(:win32) %} the constant is "\r\n" vs "\n"
<FromGitter> <Blacksmoke16> i'd still argue it's not worth having as a shard
<FromGitter> <Blacksmoke16> eh, at that point you're testing the macro flag logic more than your code
<FromGitter> <Blacksmoke16> as you'd need to do the same thing for the test afaik
<postmodern> in order for documentation to pick up the constant, should I set the constant inside of the {% if %} block, or use the {% if %} to return the literal value?
<FromGitter> <Blacksmoke16> i'd prob do the former
<postmodern> is the documentation generator macro aware?
<FromGitter> <Blacksmoke16> it doesnt need to be, since the const will be defined when the doc code runs
<postmodern> should the constant comment be inside the macro block or outside just above it?
<postmodern> sorry for all of the questions, i want the PR to get accepted without lots of edits
<FromGitter> <Blacksmoke16> oh wait i lied, `File::NULL` is defined with the latter syntax, so I'd just follow that
<postmodern> i actually wonder if i should also add a CRLF constant (it's also used by HTTP)
<postmodern> that should probably be a separate followup PR, i guess
<postmodern> hmm even setting @reader before and after `yield self` isn't tipping off the compiler
postmodern has quit [Remote host closed the connection]
deavmi has quit [Ping timeout: 245 seconds]
deavmi has joined #crystal-lang
ur5us has quit [Ping timeout: 245 seconds]
postmodern has joined #crystal-lang
<postmodern> ah ha, apparently i have to initialize @reader at the very top of #initialize, which seems like something the compiler shouldn't require
<postmodern> ah ha, found a workaround. I defined the properties/getters with property! / getter!
<postmodern> would be cool if you could define methods that optionally accept a block without having to overload the mehtod
<postmodern> like `,&block : (self) -> | Nil = nil)`
<postmodern> think i found a compiler bug when initialize a class that's fetched from a Hash
<postmodern> the compiler seems to try to infer the Hash type by assuming the base class of all the classes in the Hash'es values, not a composite of all of the values
MiaChillfox has quit [Ping timeout: 245 seconds]
postmodern has quit [*.net *.split]
Liothen has quit [*.net *.split]
pixel has quit [*.net *.split]
xybre has quit [*.net *.split]
xyhuvud has quit [Quit: No Ping reply in 180 seconds.]
Liothen has joined #crystal-lang
postmodern has joined #crystal-lang
xybre has joined #crystal-lang
pixel has joined #crystal-lang
yxhuvud has joined #crystal-lang
ur5us has joined #crystal-lang
<FromGitter> <jrei:matrix.org> For the nil, you can also have a raise in the getter method so you don't need `not_nil!`
<FromGitter> <jrei:matrix.org> You'll have better errors than `getter!` if the `def` are manually created
<postmodern> is there a performance hit by calling the getters vs calling the @ivars ?
<FromGitter> <jrei:matrix.org> In fact I'm wrong now https://github.com/crystal-lang/crystal/blob/6d9a1d583/src/object.cr#L543
<FromGitter> <jrei:matrix.org> The error is pretty good now, it will tell that the instance var is nil, instead of just "Nil assertion error"
<FromGitter> <jrei:matrix.org> postmodern: it is negligible. Inside the class I always use @ivar because I see directly it is an ivar
notzmv has quit [Ping timeout: 250 seconds]
<postmodern> `Error: no overload matches 'Proc(Tuple(String, Array(String) | Nil, Array(String) | String | Nil), Nil)#call' with types String, Nil, Nil` what am i missing here? shouldn't `block.call(index.to_s, nil, nil)` match the Proc signature?
<postmodern> hmm turns out it was due to the method being called like `obj.method do |*args|` changing it to individual block arguments fixed the error
ur5us has quit [Ping timeout: 245 seconds]
postmodern has quit [Quit: Leaving]
deavmi_ has joined #crystal-lang
deavmi has quit [Read error: Connection reset by peer]
deavmi has joined #crystal-lang
deavmi_ has quit [Read error: Connection reset by peer]
Guest56 has joined #crystal-lang
Guest56 has quit [Client Quit]
notzmv has joined #crystal-lang
Volk has joined #crystal-lang
Volk has quit [Changing host]
Volk has joined #crystal-lang
<FromGitter> <Dan-Do> it seems the regex `\w` does not care unicode characters
<FromGitter> <Dan-Do> `/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?@\[\]^_`{|}~]/`
<FromGitter> <Dan-Do> oops, `Error: invalid regex: PCRE does not support \L, \l, \N{name}, \U, or \u at 2`
<FromGitter> <Dan-Do> neat and simple one `/[^\p{L}]/`
Volk has quit [Quit: See you next time!]
Volk has joined #crystal-lang
Volk has joined #crystal-lang
Volk has quit [Changing host]
Volk has quit [Remote host closed the connection]
Volk has joined #crystal-lang
Volk has quit [Changing host]
Volk has joined #crystal-lang
Volk has quit [Quit: See you next time!]
Volk has joined #crystal-lang
Volk has quit [Changing host]
Volk has joined #crystal-lang
Volk has quit [Client Quit]
Volk has joined #crystal-lang
Volk has quit [Changing host]
Volk has joined #crystal-lang
Volk has quit [Client Quit]
Volk has joined #crystal-lang
Volk has quit [Changing host]
Volk has joined #crystal-lang
Volk has quit [Quit: See you next time!]
postmodern has joined #crystal-lang
Peter0x44 has quit [Ping timeout: 268 seconds]
<postmodern> wait, "".split("/") returns [""] instead of [] ?
<postmodern> is this desired behavior? ruby returns a [], but other languages such as JS return [""]
<FromGitter> <Blacksmoke16> `"".split("/", remove_empty: true)`
<postmodern> ah, interesting
<postmodern> also is there a constant for the $PATH separator char? On Windows it's actually ';', but on *nix it's ':'. Ruby defines a File::PATH_SEPARATOR constant for this.
<FromGitter> <Blacksmoke16> not that im aware of
<postmodern> and there's no class that handles parsing $PATH for me?
<FromGitter> <Blacksmoke16> https://crystal-lang.org/api/master/Regex.html#escape%28str%29%3AString-class-method
<FromGitter> <Blacksmoke16> oops, wrong window 🙈
<FromGitter> <Blacksmoke16> also not that im aware of
<FromGitter> <Blacksmoke16> might be some shards out there tho :shrug:
<postmodern> will open an issue and submit a PR, seems like a useful thing to have access to, esp since we're gaming for win32 support
<straight-shoota> There's `Process::PATH_DELIMITER`
<FromGitter> <Blacksmoke16> oh nice
<FromGitter> <Blacksmoke16> kinda related, whats the purpose of having `File:: SEPARATOR` and `File:: SEPARATOR_STRING`?
<FromGitter> <Blacksmoke16> ofc they're two separate types, but seems kinda redundant
<postmodern> is there an equivalant of Gem.user_home which attempts to find the home directory even if $HOME isn't set? (it also checks $HOMEDIR on windows)
<straight-shoota> @Blacksmoke16 No idea why there are two constants. I agree they're redundant
<straight-shoota> postmodern, there's `Path.home` but it currently only understands HOME
<FromGitter> <Blacksmoke16> i guess it was back when you couldn't do certain things with `Char`s?
<postmodern> how would you get the superclass of the class which a module is included into, from the context of a method in that module?
<FromGitter> <Blacksmoke16> wouldnt it just be `{{@type.superclass}}`?
<postmodern> @type seems to point to the module itself, not where the module is extended into
<FromGitter> <Blacksmoke16> ohh right, do it within a `macro included` hook
<postmodern> hmm that might be tricky, since i have this logic in a method which checks a class variable, then falls back to asking the superclass, iff it also extends ClassMethods
<FromGitter> <Blacksmoke16> 🤔
<postmodern> i suppose i could set the class variable in `macro extended`
<postmodern> er that only would work once though, i need this to cascade through the class hierarchy
<FromGitter> <Blacksmoke16> sounds like a pita
<postmodern> A extends the module, B sets the class variable, D then checks its class variable, then calls C which checks its class variable, then finally calls B
<postmodern> oh wait, i can add an inherited hook in the macro extended hook
ur5us has joined #crystal-lang
taupiqueur has joined #crystal-lang
<postmodern> i forget, whats the Class+.class syntax called again?
hightower2 has joined #crystal-lang
<postmodern> is there a way of forcibly setting ivars from outside of an object? obj.@ivar = ... doesn't work.
<FromGitter> <Blacksmoke16> technically yes
<FromGitter> <Blacksmoke16> but deff not something you want to do arbitrarily
<postmodern> this is for some tests. i could expose them using a property, but i'd rather keep the ivars internal
<FromGitter> <Blacksmoke16> make the setters protected? then could use them within the namespace of your stuff, but someone couldnt use them outside of it
<FromGitter> <Blacksmoke16> oh, if it's just for specs, monkey patch them in?
taupiqueur has quit [Remote host closed the connection]
taupiqueur has joined #crystal-lang
ur5us has quit [Ping timeout: 252 seconds]
<postmodern> is there some easier way to define proxy methods for a bunch of overloaded methods in another class?
<postmodern> context: I'm trying to forward IO methods, like #gets, to a custom stdin object
<FromGitter> <Blacksmoke16> https://crystal-lang.org/api/master/Object.html#delegate(*methods,toobject)-macro
jhass|off is now known as jhass
<postmodern> do people typically write tests for delegated methods or just trust they delegate to the correct end-point?
<FromGitter> <Blacksmoke16> if you're using that macro prob safe to assume it'll be ok
<postmodern> how does one get the uid of the process? not seeing a Process.uid
<FromGitter> <oprypin:matrix.org> postmodern, ref https://github.com/crystal-lang/crystal/pull/7822
<postmodern> ah it's another POSIX related thing
<FromGitter> <oprypin:matrix.org> yea. seems rejected ⏎ but ⏎ https://github.com/crystal-lang/crystal/pull/7822#issuecomment-562795667
<postmodern> would be useful to have a cross-platform Process.uid like method for determining if the process has admin rights or not
taupiqueur has quit [Quit: taupiqueur]
ur5us has joined #crystal-lang
postmodern has quit [Read error: Connection reset by peer]
hightower2 has quit [Ping timeout: 252 seconds]
postmodern has joined #crystal-lang
<postmodern> does OptionParser supports typed options?
<FromGitter> <Blacksmoke16> no, all the values are strings unless you do something additional to them
<postmodern> also is there a Date class?
<FromGitter> <Blacksmoke16> `Time`
<postmodern> grumble why can't you use Value in a type union?
<postmodern> i want to say `Proc(Object) | Value` for something that can either be a callable Proc or a literal Value
<FromGitter> <Blacksmoke16> not possible
<FromGitter> <Blacksmoke16> would have to prob use some wrapper type with a generic
taupiqueur has joined #crystal-lang
Volk has joined #crystal-lang
Volk has joined #crystal-lang
Volk has quit [Changing host]
<postmodern> how do you specify "an object that can be #dup'ed or nil" in a type signature?
<FromGitter> <Blacksmoke16> or nil?
<postmodern> yes, or nil indicating no value has been set
<FromGitter> <Blacksmoke16> would prob have to do something like
<FromGitter> <Blacksmoke16> ```module Dupable ⏎ abstract def dup : self? ⏎ end``` [https://gitter.im/crystal-lang/crystal?at=6164b5448c019f0d0b2ccb2a]
<FromGitter> <Blacksmoke16> but might need to use a diff name as that might conflict with the built `#dup` method
<FromGitter> <Blacksmoke16> or could just rely on the compiler to know, or use `.responds_to?`
<postmodern> this is for specifying a "default value" for a command-line option. So the range of values are things like Int, String, Array, Hash, but also `nil` indicating there is no default value given
<FromGitter> <Blacksmoke16> im pretty sure `#dup` is defined on all objects, and just returns `self` by default
deavmi has quit [Ping timeout: 245 seconds]
<FromGitter> <Blacksmoke16> nvm, latter part isn't true, but it is defined on all objects
<postmodern> also how would i specify a Proc type signature indicating it just return a value? The compiler isn't liking Proc(Object)? = nil
<postmodern> trying to port this ruby code which supports giving a default literal value or a Proc which returns a new arbitrary value
<FromGitter> <Blacksmoke16> you cant, would need to type it more specifically
<FromGitter> <Blacksmoke16> `Proc(Int32)? = nil`
<FromGitter> <Blacksmoke16> or what have you
<FromGitter> <Blacksmoke16> possibly could use some module that you could include into this, but that feels gross
<FromGitter> <Blacksmoke16> into things*
<FromGitter> <Blacksmoke16> porting things 1:1 from Ruby prob isn't going to end well
<postmodern> i don't suppose it's possible to define an arguments type based on another argument's value?
<FromGitter> <Blacksmoke16> would be worth taking some time to think about a more Crystal approach
<FromGitter> <Blacksmoke16> that is actually possible via generics i think
<FromGitter> <Blacksmoke16> as you could have a generic tied to a specific arg, and use the same generic type for another arg
<postmodern> yeah i will have to rethink this. the entry point of the code are class-level methods which annotate the options
<FromGitter> <Blacksmoke16> ```def foo(val1 : T, val2 : T) forall T ⏎ end``` ⏎ ⏎ something like that i think [https://gitter.im/crystal-lang/crystal?at=6164b89fd3ae402e4742338f]
<FromGitter> <Blacksmoke16> making a CLI builder lib?
<postmodern> might have to rewrite things as macros instead of class-methods
<postmodern> yeah
<FromGitter> <Blacksmoke16> are some shards out there for this as well fwiw
<FromGitter> <Blacksmoke16> could look how they do it maybe?
<postmodern> i was trying to grok how admiral does it, will need to grok harder
<FromGitter> <Blacksmoke16> Athena's console component handles this by using a generic to represent the type of the option, at least for the default value. Options are otherwise fetched as strings iirc, with the option to do some conversion for common types
<FromGitter> <Blacksmoke16> er nvm i lied, it just stores the default as a union, not using generics
<postmodern> i suppose `option` could become a macro that initializes an generic class, so the type information is passed in via generics
<FromGitter> <Blacksmoke16> is there actually a use case for a value to be something that isnt a like number, string, bool, nil, or an array of those?
<postmodern> possibly, maybe for like a custom hash-like class
<FromGitter> <Blacksmoke16> could always save that for later
<FromGitter> <Blacksmoke16> and think about it more
<postmodern> https://github.com/postmodern/command_kit.rb/blob/main/lib/command_kit/options/option_value.rb#L21-L103 i have a bunch of hardcoded usage strings for common things like Integer -> NUM, but also support arbitrary classes
<postmodern> but that's more to future proof the code. 99% of the time option values will be of type String, Int, and maybe sometimes Array or Hash
<FromGitter> <Blacksmoke16> cant say i ever saw a CLI that supports a hash input
<FromGitter> <Blacksmoke16> could implement it as like `key=value,foo=bar,biz=baz`
<FromGitter> <Blacksmoke16> split, split, zip and good to go
<postmodern> some CLIs will support things like --params key:value, where each pair is added to a hash table
<postmodern> also key=value,... can also be a thing. pretty sure ffmpeg supports that
<FromGitter> <Blacksmoke16> ah, that makes more sense
<postmodern> random other project i might eventually port to Crystal, is command_mapper.rb, which is like the opposite of command_kit.rb, in that it maps CLI options to attributes. I had to write a mini type system to represent things like Map, List, KeyValue, KeyValueList, Num, Hex
<FromGitter> <nanobowers> I ported Optimist to Crystal https://github.com/nanobowers/optimist.cr and it should be able to be extended to add additional receiver classes so long as they inherit from Optimist::Option, IIRC.
<FromGitter> <nanobowers> it's still a WIP, but should in theory be able to be extended to include Map/...Num/Hex receivers for cmdline opts
<postmodern> ah ha, you defined default using a generic type T (but i think you forgot to define Option as Option(T) ?)
<postmodern> TIL `macro finished` that will come in handy
<FromGitter> <nanobowers> Oh, i probably didn't do it right *at all*
<FromGitter> <nanobowers> I did encounter issues making Option a templated type, though. Had played with it for a while and hit some snag that felt nasty enough to revert all of the changes. Perhaps it was that I couldn't define untemplated subclasses? It's been a while
taupiqueur has quit [Ping timeout: 265 seconds]