Skip to content

Limitations and solutions

Philipp Schulz edited this page Sep 11, 2022 · 4 revisions

Anyolite aims to provide as much compatibility between Crystal and Ruby as possible, but there are still some limitations. Most limitation can be circumvented using annotations or dedicated wrapper functions, but a few limitations remain due to the fundamental differences between Crystal and Ruby.

The following sections will differentiate between hard limitations and soft limitations. Specifically, hard limitations are defined as 'Can not be solved without modifying Anyolite' and soft limitations as 'Can be solved without modifying Anyolite'. Hard limitations might be solved in the future, but the code modiciations to Anyolite are usually not trivial and the potential use cases are rather limited. If you absolutely need any of these limitations to be solved, pull requests or even detailed issues are highly appreciated.

Hard limitations

  • Currently, only GCC (on Linux) and Visual Studio (on Windows) are supported as compilers. This will likely change in a later release, but the number of scenarios requiring other compilers is rather sparse. Modifications of the Rakefile and the linker options (from inside Anyolite) are necessary to change this.

  • Anyolite does only work on Windows with Crystal version 1.2.0 or higher due to a specific issue which crashes Anyolite as soon as functions are bound to Ruby. There is no way to change the code to support earlier Crystal versions, so version 1.2.0 is absolutely required for using Anyolite on Windows. Other platforms may work with earlier versions.

  • The MRI implementation does currently not work with Windows due to a yet unknown problem causing crashes while calling Ruby API methods. Any help would be appreciated, as MRI has currently no priority over mruby.

Soft limitations

Function arguments

  • Procs and blocks are possible to use as arguments, but they require annotations (Anyolite::AddBlockArg or Anyolite::StoreBlockArg, depending on the situation) to the respective functions (see Passing block arguments for more details).

  • Proc arguments can currently not be optional, so overloading is also not possible. By providing separate methods with and without block arguments, this can be solved. It is possible that this might be fixed in the future.

  • Splat arguments and arbitrary keyword arguments are not possible to bind. This is a consequence of the design differences between Crystal and Ruby and can only be emulated using Arrays and Hashes as arguments for a proxy method.

  • Default values to function arguments are always instantiated as soon as the function is called (regardless of whether the argument was actually passed). This is a result from the way Anyolite is implemented. Try to avoid overly complex default arguments (a workaround would be to set nil as the default and then handle the value accordingly).

  • Default arguments need to be known to Anyolite using their full path (e.g. MyModule::ThatConstant instead of ThatConstant, even if the function is defined in MyModule), due to a code limitation in the Anyolite implementation (this might change in the future, but is currently not prioritized). Specializing any method with the full default arguments will fix this problem.

  • Some unions and generics also need to be passed using their full path (see bullet point above). Specialization is again possible here.

Data types

  • Integers larger than 64 bit will likely cause problems with Ruby. However, Anyolite can be modified to use a Bignum gem (either by modifying the mruby and Anyolite config files or by installing the Ruby gem on MRI), although this is not tested yet.

  • Symbols are not compatible between Crystal and Ruby due to their fundamental differences. However, Ruby Symbols passed as arguments will be converted into Crystal Strings and Crystal Symbols will be converted into Ruby Symbols.

  • Structs are automatically wrapped using a dedicated wrapper class. They are technically immutable due to the way the work in Crystal, but it is possible to replace the content of directly wrapped structs with another structs using their instance methods. If a struct is the attribute of another wrapped class, this approach does not work and the struct needs to be duplicated, modified and then reassigned.

  • Arrays, Hashes and Strings passed from Crystal to Ruby (or vice versa) are immutable objects. Changing this directly would require modifications of the Ruby implementation, but Crystal container classes can be used as a workaround.

  • Pointers as arguments are possible, but not recommended. Technically, they should be tracked by the Crystal Garbage Collector, but they are not tested extensively and can still be abused potentially. Writing a dedicated wrapper class is the safer way here.

  • Slices and Bytes are not supported at this time, but wrapper classes can be used here as well.

Other limitations

  • Only one function with a specific name can be wrapped, since Ruby technically has no overloading. Specializing the function argument as a Union and avoiding illegal calls (which will be tested at compilation) can however mimic overloading.

  • Private constants will trigger errors if wrapped. They need to be excluded manually from their classes and modules using Anyolite::ExcludeConstant.

  • There are some problems with Crystal libraries injecting Anyolite-incompatible methods into classes with many descendants like Object. A prominent example is the colorize library. A possible solution (to avoid excluding colorize for each single class separately by hand) is to exclude the problematic methods using the annotation @[Anyolite::ExcludeInstanceMethod("colorize")] on the modified classes or modules (in the given example this would be module ObjectExtensions).