The Crystal Programming Language Forum

Please help with TCP Chat Server example

I’m a bit confused on your approach here. Whats the point of creating the module if you’re just going to include it into the top level namespace? At that point you could just define all these as methods on the top level and not even use a module in the first place. Also having a module just to hold a class variable that is used by another module is also kinda weird. You could just use a constant on the top level and get the same effect.

Namespacing is a good practice as it avoid name conflicts, especially if you release this code as a shard. Otherwise, depending on what your end goal is, I’m not sure this is the best approach.

I love to have short names in the small program without a namepace. I have to use a module or a class with a class var because there are no global vars in Cystal, a constant would not help me bacause the var changes it values during the program runs.
I think global vars as a language feature are nice, think about having small “scripts” for system administration. You may need a global var which is visible in every method of your small program.
For larger programs I think that global vars are not good and should be avoided.

icr:>$message = "test"
$global_variables are not supported, use @@class_variables instead

Is it a goal for the Crystal language to be used for system administration “scripts” ? There having to write classes or modules for scripts with about 10 lines most of them in the toplevel is a sort of boilerplate.

@@test = "aha"
     ^
Error: can't use class variables at the top level

Ruby just gives a warning in this case
irb> @@test = “aha”
(irb):1: warning: class variable access from toplevel
=> “aha”

Actually a constant would work, because you don’t reassign the “global”, you only access it and modify it using methods.

Constants prevents you from doing:

Const = [1]
Const = [2] # error, you're trying a reassign the constant!

But you can do:

Const = [1]
Const.clear # ok

Ok, thank you - that works.
I was not aware of this “fine” difference. So a constant can be changed during runtime by methods - but possibly not all operators ? Is there some doc about which operators and methods would allow to change the values of a constant ?
I started reading here and missed this useful information:
https://crystal-lang.org/reference/syntax_and_semantics/constants.html
Thank your for this Crystal lesson.

module Myclients
   extend self
   #The Array AC holds TCP socket information and changes during runtime of program !
   AC = Array(TCPSocket).new  #Constant array for current client connections
   def add_client(str)
       AC << str
       clients_state
   end
   def del_client(str)
       AC.delete str
       clients_state
   end
   def get_clients
       AC
   end    
   def clients_count : Int32
       AC.size
   end    
   def clients_state
       puts AC
       puts "Number of Clients Online: #{clients_count}"
   end
end   
include Myclients

Compare

foo = [] of Int32
bar = foo
bar << 1
foo # => [1]

and

FOO = [] of Int32
bar = FOO
bar << 1
FOO # =>  [1]

Constants forbid reassignment and are not lexically scoped. They do not change the behavior of the objects assigned to them. This is the same in virtually every language. Ruby has the freeze API to mark an object as immutable, and it’s good practice to freeze objects that are assigned to constants. Crystal has no such API (yet).

It’s important to think about the behavior of the identifier (Constant, @@class_variable, @instance_variable, local_variable) and the value that’s assigned to it independently.

A constant works for reading and writing via methods.

Not exactly I think.

the constant in this specific case is of the type Array.
It stays the same array the whole time.
No other, different array was assigned to the constant.
BUT the content of the Array can change because it is the content of the array and not the variable itself that changes.

for example, if you have a constant NUMBER = 1
you cannot change that number because it is a simple(can’t remember the correct name) type.

But if you have an object that is assigned to the constant, then this only means that you cannot assign a different Object to this constant.

It does not prevent you from using this objects methods, and if those change the internal state of the object, that is fine.
That is why jhass brought up the freeze method, which crystal currently lacks, but can be used in other languages to make objects unchangable.

I hope that clears things up a bit.

Should this give a compiler Error ? (Error: already initialized constant)
or runtime error ?

STR = “hello crystal”
p! STR
STR.insert(1,“insert”)
p! STR

$ crystal const1.cr
STR # => “hello crystal”
STR # => “hello crystal”

Hmm. I would have guesssed this would work :slight_smile:

Ahh, I misunderstood. This does not throw the compile error you mentioned in the first line.
It just does not print what you expect.

edit:
Strings in Crystal are immutable. see: https://crystal-lang.org/api/master/String.html
That means you will never change the original string, but always return a new one.
(which, as we noticed above, you cannot assign to an already assigned constant.)

No: https://carc.in/#/r/927k

String#insert returns a new string, you’re just ignoring that return value.

Also very conceptually, please try to understand what I wrote above, especially the last sentence. You’re still conflating the identifier with the value that’s assigned to it.

1 Like

If I’m not mistaken, the socket messages transits in bytes, converts to a string, then if we forward the message it’s reconverts back to bytes. :crazy_face:

With Go and Node.js I use bytes, not string. It’s more effective.

I only see examples of Crystal chat with messages typed in String. Why not Bytes (Slice(UInt8)) instead String for the messages?

Does anyone have an example using Bytes messages?

I don’t quite understand the difference. Of course a stream of bytes goes over the wire. Crystal strings are always UTF-8 encoded. What is a string if not a chunk of bytes?

Which parameters must follow a class_property for a Filedescriptor ?
Is this possible ?
module xx
class_property someint : Int32 = 0 #this works
class_property bashinputfd : IO::FileDescriptor+ ??? = ???

If I try Int32, i get this error on assignment:
Error: no overload matches ‘Mc.bashinputfd=’ with type IO::FileDescriptor+

mmm, what are you trying to do? I assume just read from stdin or something? In which case you would prob want to do something like class_property input : IO = STDIN.

Thank you that works, its just to simple :slightly_smiling_face:

I don’t quite understand the difference. Of course a stream of bytes goes over the wire. Crystal strings are always UTF-8 encoded. What is a string if not a chunk of bytes?

I have not checked in Crystal but in general this has a cost on memory and number of messages per second.

An unnecessary cost in this case. When a message just needs to be routed, this avoids making 2 unnecessary conversions (bytes -> string, string -> bytes).

I still don’t quite follow but sockets’s are IOs in Crystal so you have the full arsenal of that available. You certainly can send and receive other things than String.

Yes io#read_byte, io#read_fully?, io#write_bytes, … From there we need to build the highest level methods (read line and backpressure management). Very painful, especially since there is already something in Crystal’s std. That’s why if someone has examples or interesting docs for that in Crystal, I’m interested.

@nico Do you have some benchmarks that prove what you say? In Crystal String#to_slice doesn’t allocate memory at all. And then gets and other IO methods build a string efficiently, it’s not like they are reading to a slice and then copying everything to a string.

Thanks for your reply. Yes gets and other IO methods build a string efficiently.

But in my case, it’s no use.
I’m not talking about String#to_slice. It’s the combination of conversions (necessary codes for dealing with Bytes and String, instead only Bytes): Slice#to_s, inspect, join, new String builder, encoding, etc)

Sorry I didn’t do any benchmarks, I just looked at the implementation, there’s bound to be some performance (and memory) loss to deal with between Bytes and String. I’m mainly looking for a way to handle the messages with Bytes only.

As long as to refacto a socket server from Go to Crystal, I prefer to start on a good basis.