Skip to content

Instantly share code, notes, and snippets.

@kornelski
Last active December 24, 2017 09:30
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kornelski/0f7ebcec230117ab52c959fe0b090335 to your computer and use it in GitHub Desktop.
Save kornelski/0f7ebcec230117ab52c959fe0b090335 to your computer and use it in GitHub Desktop.
Language Adding files from filesystem to the build How? Unfinished files break the program?
Rust Explicit mod declaration No
CommonJS Explicit require import No
ES6 Explicit import import No
Python Explicit import import No
Lua (new) Explicit require import No
PHP Explicit require or use via autoload import or any use No
Perl Explicit use import No
D Explicit import import No
R Explicit import import No
Ocaml Explicit open import No
Swift Implicit in the language, explicit in the IDE Files are added via IDE Depends — it's possible to add files without compiling them
Java Explicit import (but IDE's may write imports automatically) import No, unless IDE auto-adds them
Go Implicit automatic Yes
C# Implicit in the language, explicit in the IDE Via IDE If glob is used in IDE
Ruby Explicit require import No
Visual Basic Implicit Files are added via IDE Yes?
C, C++ Depends on build system/IDE, usually explicit import (headers) + build config Usually not
Clang C++ modules Explicit import import No
Haskell Explicit import import No?
Language Declarations of existence in the parent module Declarations in the module itself Use of the module in the same package Use of the module from another package Notes
Rust mod foo; or
pub mod foo;
pub fn bar(){} mod foo; or
use foo;
extern crate pkg;
use pkg::foo;
CommonJS none exports.bar = function(){} const foo = require('./foo'); const foo = require('pkg/foo');
ES6 none export function bar(){} import foo from './foo'; import foo from 'pkg/foo';
Python none def bar():
  pass
import foo from pkg import foo
Lua (new) none local foo = {}
function foo.bar()
end
return foo
local foo = require "foo" local foo = require "foo"
PHP none namespace Pkg\Foo;
function bar() {}
use Pkg\Foo; use Pkg\Foo;
Perl none package Pkg::Foo;
sub bar {}
use Pkg::Foo; use Pkg::Foo;
D none module pkg.foo; void bar(){} import pkg.foo; import pkg.foo;
R none foo <- module({bar <- function() NULL}) import("foo") import("pkg", "foo")
Ocaml none let bar () = () open Foo #require "pkg";; open Foo
Swift none func bar(){} none import Pkg Packages aren't explicitly divided into modules
Java none package com.example.pkg;
public class Foo{
public static void bar(){}}
import com.example.pkg; import com.example.pkg; Packages aren't explicitly divided into modules
Go none package pkg;
func Bar(){}
none import "pkg"; Packages aren't explicitly divided into modules
C# none namespace pkg {
public static class Foo{
public static void bar(){}}}
optionally using pkg; optionally using pkg; Packages aren't explicitly divided into modules
Ruby none def bar()
end
require_relative 'foo' require 'pkg' Packages aren't explicitly divided into modules
Visual Basic none Namespace Foo
Public Sub Bar()
End Sub
End Namespace
Imports Foo Imports Foo VB also has modules, but they aren't namespaced
C none
(headers tho)
void bar() {} #include "foo.h" #include <pkg/foo.h> Not real modules
C++ none
(headers tho)
namespace foo {void bar() {}} #include "foo.h" #include <pkg/foo.h> Not real modules
ObjC 2 none
(modulemap tho)
void bar() {} #include "foo.h" @import pkg.foo Only used by Apple?
Clang C++ modules none
(modulemap tho)
module pkg.foo; export void bar() {} import pkg.foo import pkg.foo Experimental/working draft
Haskell none module Pkg.Foo(bar) where
bar :: ()
bar = ()
import qualified Pkg.Foo as Foo import qualified Pkg.Foo as Foo
@ahmedcharles
Copy link

Language Importing external libraries How? Unfinished files break the program?
Rust Explicit extern crate foo; declaration No
Language Adding local files for use by the current library How? Unfinished files break the program?
Rust Explicit mod foo; declaration No
Language Declarations in the module itself Use of the module in the same package Use of the module from another package Notes
Rust pub fn bar(){} mod foo; extern crate pkg;

use is never required to use code, it's only a way of renaming something. The equivalent of extern crate pkg; use pkg::foo; in JS, would be const pkg = require('pkg'); const foo = pkg.foo;, assuming I get the syntax right. Your comparison places Rust at a disadvantage because extern crate pkg; use pkg::foo; introduces two names and therefore, has to be more complex than what you currently have for JS, which is const foo = require('pkg/foo'); and only introduces a single name. If you only want a single name, use extern crate pkg; and if you want two names, use the more complex version.

@yasammez
Copy link

yasammez commented Aug 3, 2017

This is a great list, thank you very much! Just out of curiosity, I'd like to see Erlang and Elixir added to the list (because I have absolutely no idea how they do it).

@Boddlnagg
Copy link

Boddlnagg commented Aug 3, 2017

The table seems a bit wrong with respect to how it works in C#. Let me try to explain this a little ... (Update: I restructured this a bit)

Adding files from the filesystem to the build is done by explicitly naming each file to the build system (resp. compiler), unless you have an include glob **/*.cs in your project file (which only works well since VS2017, but seems like it is becoming a new default, at least in .NET Core). With the include glob, adding files is implicit by just creating the files. Without it, the IDE is responsible for adding newly created files to the project (this seems to be similar to what the table says about Swift, though I know nothing about Swift).

Using a module in the same package (= "assembly" in .NET lingo) requires no using, using only gives you direct access to names. So you can write new List() instead of new System.Collections.Generic.List() when you have a using System.Collections.Generic;. And it gives you implicit access to extension methods from those namespaces that you are using (similar to traits that you use in Rust).

Using a module in another package also does not require using (if you use absolute namespaced paths), you only need to tell your build system about the package (similar to Cargo.toml).

Additional bits that are not part of the tables, but that I want to mention for completeness:

The mapping from namespaces to files is arbitrary, a file can contain as many (nested) namespaces as you like, and you can (re-)open any namespace from any file. The usual way, however, is to have one directory per (sub-)namespace, which would correspond to "one module = one directory" in Rust. Namespaces don't have any meaning for visibility in C#, though, and neither have files. Visibility is solely implemented at the class level (and this works well because everything has to be in a class in C#, there are no free functions directly inside a namespace). So classes (and items within classes) can be visible either to the world (public), or to the package (internal). Items within classes can additionally be visible only to the class itself (private) or to the class and all subclasses (protected).

Re-exporting is not supported in C#, and indeed was one of the hardest parts for me when I began to learn Rust (because of its unfamiliarity). Now that I have understood it, I don't want to miss its flexibility, though ...

@kornelski
Copy link
Author

@Boddlnagg thanks for the info!

@ahmedcharles I've added extern create with use, because that's how I see it used in practice in projects that have modules themselves. I know you could structure your project to only use crate from one module and/or use full absolute paths to it everywhere, but that would be only to prove a point that you can, rather than representative of how it's done in general.general.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment