The Crystal Programming Language Forum

Operate on a collection, passing a Proc of mixed types?

Hello, I’m trying to sort an Array of file pathes, using different procs. But it seems to be a problem that not all the procs return value of the same type. Is there a way to handle this without blowing out the code?

Simplified example (https://carc.in/#/r/8j4f):

def mtime(path)
  File.info(path).modification_time.to_unix
end

def sorting_method
  [
    ->(path : String) { mtime(path) },
    ->(path : String) { -mtime(path) },
    ->(path : String) { path.downcase },
  ].sample
end

files = [] of String
files.sort_by! &sorting_method

The error:

14 | files.sort_by! &sorting_method
                     ^-------------
Error: expected a function type, not (Proc(String, Int64) | Proc(String, String))

What do you expect to happen? You could fix the primary error by casting the array entries to String -> String | Int32. But the next problem is that it results in comparing String with Int32 which obviously doesn’t work.

def mtime(path)
  File.info(path).modification_time.to_unix
end

files = [] of String

case rand(3)
when 0 then files.sort_by! { |path| mtime(path) }
when 1 then files.sort_by! { |path| -mtime(path) }
when 2 then files.sort_by!(&.downcase)
end

Yes, that’s what I ended up with, but thought it was better to ask. Thanks.

I expected the sample to work. Despite the value returned by sorting_method being represented as union of types, practically it’s always consistent throughout sorting, and compare of mixed types never really happens. Apparently, the only reason the snippet doesn’t work is the compiler being unable to figure that out. Maybe that changes one day.

Hello, what do you think about a new concept that might help with the case?

@[ExclusiveUnion]
def sorting_method
  [
    ->(path : String) { path.size },
    ->(path : String) { path.downcase },
  ].sample
end

puts typeof(sorting_method) # => (Proc(String, Int32) || Proc(String, String))
#                                                      ^
#                                                      | ADDITIONAL BAR

The type (Proc(String, Int32) || Proc(String, String)) is the way to tell the compiler that despite the returnted type being a Union, it never going to return mixed types. so it can be used for sorting here. Maybe that could be helpful somewhere else.

I think the compiler could call one proc or the other depending on the type, like it does with regular arguments. It’s just something low priority so it probably won’t be implemented.