Andra
Andra

Reputation: 1392

Why I can't define a function in the `lib` scope?

For example, this code won't compile

def double
  x * x
end

lib LibC
  # Error
  def double
    x * x
  end
end

I know that we can use module, but we can't have the same LibC name as both module and lib

lib LibC

end

module LibC
  def double
    x * x
  end
end

Is there any solution for this problem?

Upvotes: 0

Views: 103

Answers (1)

Jonne Haß
Jonne Haß

Reputation: 4857

Why is this a problem for you? :)

lib defines a binding to an external library. Technically it does so by defining function signatures of external symbols that the linker will resolve. Additionally it allows the definition of structs with C ABI semantics, enums for mapping a series of constants used in an external library interface into a useful grouping and aliases to make it easier to map Crystal types to C types in a binding definition.

When binding a library, this lowlevel lib definition should never be the end result for the user of your binding! Instead you should define at least a thin wrapper transforming the external API into a more idiomatic Crystal version.

So say we're writing a binding to the fictional libsaucepan you would start out with a lib definition, maybe like this:

@[Link("saucepan")]
lib LibSaucepan
  alias Pan = Void*
  fun aquire : Pan
  fun add_water(pan : Pan, ml : LibC::Int)
  fun cook(pan : Pan)
  fun is_done(pan : Pan) : LibC::Int
end

You wouldn't stop here, but maybe create a class that wraps the API:

class Saucepan
  def initialize
    @pan = LibSaucepan.aquire
  end

  def add_water(ml : Int)
     LibSaucepan.add_water(@pan, LibC::Int.new(ml))
  end

  def done?
    LibSaucepan.is_done(@pan) == 1
  end

  def cook
    LibSaucepan.cook(@pan)

    until done?
      sleep 1
    end
  end

  def heat_water(ml : Int)
    add_water(ml)
    cook
  end
end

This wrapper is where you would put any helper methods :)

Upvotes: 4

Related Questions