Search code examples
importsmalltalkgnu-smalltalk

Importing files in GNU Smalltalk


I'm new to GNU Smalltalk. I know that in most programming languages, there's an import/#include/require command that gives one source file access to the contents of another. My question is, how do I import one file into another in GNU Smalltalk? Any help would be appreciated. Thanks!


Solution

  • I think there are two good answers:

    1. FileStream fileIn: 'afile.st'.

    2. In GNU Smalltalk, you don't import/include/require one source file into another source file.

    Explaining the first answer

    Suppose I have the file foo.st:

    "foo.st"
    Object subclass: Foo [
        foo [^ 'I am Foo from foo.st']
    ]
    

    If I want to use the class Foo from the code I am writing inside bar.st, I can use FileStream fileIn: 'foo.st' in the init method of Bar:

      "bar.st"
      Object subclass: Bar [
        | barsFoo |
    
        Bar class >> new [
            | r |
            r := super new.
            ^ r init.
        ]
    
        init [
            "Combines the contents of foo.st with the current image
             and assigns a new Foo to  barsFoo."
            FileStream fileIn: 'foo.st'.
            barsFoo := Foo new.
        ]
    
        bar [
            ^ 'I am bar from bar.st'
        ]
    
        foo [
            ^ barsFoo foo.
        ]
    ]
    

    Using these classes looks like:

    $ gst
    GNU Smalltalk ready
    
    st> FileStream fileIn: 'bar.st'
    FileStream
    st> b := Bar new
    a Bar
    st> b foo
    'I am Foo from foo.st'
    

    So far, it all looks like an ordinary import/include/require. But it's not really because the FileStream fileIn: 'foo.st' inside of init occurred at runtime and so I can type:

    st> f := Foo new
    a Foo
    st> f foo
    'I am Foo from foo.st'
    

    Explaining the second answer

    The reason I get a new Foo after importing bar.st is because FileStream fileIn: 'bar.st' combines the contents of bar.st with the current image.

    Though GNU Smalltalk uses the abstraction of source code files. The underlying abstraction of Smalltalk is the image not the file and that is as true of GNU Smalltalk as any other Smalltalk system. The lack of a traditional IDE does not change the primacy of the image. For me, this has been a hard abstraction to get my head around as a new Smalltalk user in general and a new GNU Smalltalk user in particular.

    This means that the ordinary low-level step-by-step method of managing Bar's dependency on Foo is by creating an image that already includes Foo first:

    $ gst
    GNU Smalltalk ready
    
    st> FileStream fileIn: 'foo.st'
    FileStream
    st> ObjectMemory snapshot: 'foo.im'
    "Global garbage collection... done"
    false
    st>
    

    Now I can start the image that already contains Foo:

    $ gst -I foo.im
    GNU Smalltalk ready
    
    st> f := Foo new
    a Foo
    st> f foo
    'I am Foo from foo.st'
    

    So long as I am not actively developing Foo in parallel with Bar I can change its init to:

    init [
      barsFoo := Foo new.
    ]
    

    I could also create a new image with both Foo and Bar:

    $ gst -I foo.st
    GNU Smalltalk ready
    
    st> FileSteam fileIn: 'bar.st'
    FileStream
    st> ObjectMemory snapshot: 'foobar.im'
    

    Building a system from latest source

    It is possible to create an object that reads the latest version of both files from disk:

    Object subclass: FooBarBuilder [
        FooBarBuilder class >> new [
            | r |
            r := super new.
            ^ r init.
        ]
    
        init [
            FileStream fileIn: 'foo.st'.
            FileStream fileIn: 'bar.st'.
        ]
    ]
    

    And build an image:

    $ gst
    GNU Smalltalk ready
    
    st> FileStream fileIn: 'foobarbuilder.st' 
    FileStream
    st> ObjectMemory snapshot: 'foobar.im'
    "Global garbage collection... done"
    false
    st> 
    

    Using the new image foobar.im allows me to bring in the latest versions of Foo and Bar each time I create a new FooBarBuilder. Not exactly beautiful and kind of kludgey, but it will do some of the work of a real build system.

    Packaging it all up

    Instead of keeping track of the multiple images (foo.im, foobar.im) GNU Smalltalk's package system can be used to import all the necessary files into a 'clean' Smalltalk runtime. It starts by creating a package.xml file:

    <package>
      <name>FileImport</name>
      <file>foo.st</file>
      <file>bar.st</file>
      <file>foobarbuilder.st</file>
      <filein>foo.st</filein>
      <filein>bar.st</filein>
      <filein>foobarbuilder.st</filein>
    </package>
    

    The next step is 'publishing' the package so GNU Smalltalk can find it using gst-package. Here I publish it to my home directory in Linux rather than the system wide location (/usr/share/gnu-smalltalk/ on Ubuntu 16.04):

    ~/smalltalk/fileimport$ gst-package -t ~/.st package.xml
    ~/smalltalk/fileimport$ gst
    GNU Smalltalk ready
    
    st> PackageLoader fileInPackage: 'FileImport'
    "Global garbage collection... done"
    Loading package FileImport
    PackageLoader
    st> Foo new
    a Foo
    st>
    

    Concluding Remarks

    There is no free lunch. GNU Smalltalk gains something by making it easy to work with files in familiar ways. The price is that files don't integrate particularly well with the abstraction of images and the expectation that development will mostly occur by modifying running images.

    There's no free lunch. At some point the gains from traditional Smalltalk IDE's are likely to outweigh the familiarity of working with source code files due to the impedance mismatch.