Crystal: Add a way to know the main file of compilation

Created on 23 May 2016  路  7Comments  路  Source: crystal-lang/crystal

I was trying to check if file was required or it is the file passed to the compiler to be build, but I didn't find any way of doing this.

I propose implementing __MAIN__ which equals __FILE__ of the main file of compilation.

As example, I tried somethink like this:
./foo.cr

def square(x)
  return x * x
end

if __FILE__ == $0
  puts "This file defines the square function"
  puts "example: square(12) returns #{square(12)}"
end

./bar.cr

require "./foo"
puts square(100)

What I expected (Ruby's behaviour) is

$ crystal foo.cr
This file defines the square function
example: square(12) returns 144
$ crystal bar.cr
10000

but Crystal's actual behaviour is

$ crystal foo.cr
$ crystal bar.cr
10000

because $0 will never match __FILE__ (even with $ crystal run bar.cr $0 is ~/.cache/crystal/crystal-run-bar.tmp).

Since Crystal is compiled and generates a binary file, I like $0's current behavior, so adding a new way to know the main file, which also does not depends on which the crystal command is run or build, seems good to me.

I know this is not something crucial and even in my real use case separate into two files makes the work, but I think this is something nice to have.

Thanks, and sorry if this is too long for something that simple :laughing: .

draft compiler

Most helpful comment

I'm with @jhass here, I don't like that pattern either.

But besides not liking it, I'll try to give a more objective reason. A file should have a single responsibility, maybe defining a class or a bunch of related classes, or code with a related functionality. Mixing a main program together with some other code doesn't feel right/clean. Separating main code, like examples, in an examples directory, where each file has a name that clearly says what it does, is much better in my opinion.

All 7 comments

I never really liked that pattern, personally I always separate library code from toplevel/CLI code, into separate files.

I'm with @jhass here, I don't like that pattern either.

But besides not liking it, I'll try to give a more objective reason. A file should have a single responsibility, maybe defining a class or a bunch of related classes, or code with a related functionality. Mixing a main program together with some other code doesn't feel right/clean. Separating main code, like examples, in an examples directory, where each file has a name that clearly says what it does, is much better in my opinion.

When I gave thumbs up above, I was thinking of "to make a warning if a non-main is compiled as main", haha, but thinking about it, that would be pretty weird to happen.

I don't think we'll go that way, so I'm closing this. The recommended way is to include an examples or samples directory in a project.

Not liking something isn't a reason to forbid it, especially when it's such a widely used pattern in other programming languages like Python and Ruby. I am also convinced that there are many good use cases for this pattern outside of the ones that have been dismissed here.

And I know that all of this could be dismissed again with "just make another tiny file", but I believe a large number of tiny files actively hurts my ability to understand a project, especially at short notice.

Reusable command line tools

def main(args):
    ...
    return 0

if __name__ == "__main__":
    sys.exit(main(sys.argv))

Why intentionally limit your command line tool to be usable only by spawning a process, when instead (for free!) you could make it a reusable method that accepts an array of arguments.

I am already in the examples directory!

So I made (well, ported) a library with a big suite of examples and a nice showcase application that puts them together.

image

Each of the examples consists of one file and is runnable on its own. The problem is that if I require all of them for the integrated showcase, they'll all run one by one before the execution gets to the showcase code.
And no, I do not want to make one "runner" file per example, or drop one of the two perfectly valid use cases.
So I did a double hack to enable this. Each example is a subclass of a common base. The "runner" module queries for subclasses of this base and runs them. Each example requires it after defining one such subclass. The "showcase" imports the "runner" module before any subclasses are defined meaning that it runs nothing and consequent requires from the examples do nothing for something that already was imported. So then the "showcase" is free to do what it wants.


Now I have another use case with examples. I want one example to focus on a specific use case in a complex way, and another, more general example that happens to reuse the same code without putting focus on it. Again I insist that everything in the examples folder should be runnable by itself and I don't want proliferation of tiny files.

The hack I will do here is defining and immediately running a main function in both files. The definition of main is overwritten by the requiring file so the definition of the required file is never ran. There's a small catch -- the same main is ran twice, so just add an exit...


So uh what are we gaining by omitting this feature?
And these were just my experiences with this, there could be many more valid use cases.

Just a note: While it makes some sense to have this in interpreted languages like Ruby and Python, in a compiled language it has less benefit IMHO.

I'd say this would make a lot of sense.

One great use case for Crystal is as a "compiled scripting language" - a language you can write high-level code in and still have it run fast. Let's say you have a collection of tools written in Crystal. Being able to require across those tools without having to split the front-ends out into separate files would be extremely useful.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

asterite picture asterite  路  60Comments

benoist picture benoist  路  59Comments

chocolateboy picture chocolateboy  路  87Comments

asterite picture asterite  路  139Comments

HCLarsen picture HCLarsen  路  162Comments