💾 Archived View for blitter.com › apl-books › APLX50 › APLX-manual › www.microapl.com › apl_help › c… captured on 2024-08-18 at 20:34:12.
⬅️ Previous capture (2022-07-17)
-=-=-=-=-=-=-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>Interfacing to Ruby</TITLE> <META NAME="DESCRIPTION" CONTENT="APL language help page: Interfacing to Ruby"> <META NAME="KEYWORDS" CONTENT="Ruby,apl,aplx,apl help"> <!-- %%COMMON_HEAD%% --> <META http-equiv="Content-Type" content="text/html; charset=utf-8"> <LINK rel="stylesheet" type="text/css" href="http://www.microapl.com/styles_apl_help.css"> <!-- %%END%%--> </HEAD> <body> <table> <tr> <td width="800" valign="top" colspan="2"> <center>Topic: <A HREF="ch.htm">APLX Help</A> : <A HREF="ch_070.htm">Interfacing to other languages</A> : <A HREF="ch_070_050.htm">Interfacing to Ruby</A> </center> <center> [<A HREF="ch_070_060.htm">Next</A> | <A HREF="ch_070_040.htm">Previous</A> | <A HREF="ch.htm">Contents</A> | <A HREF="help_index.htm">Index</A> | <A HREF="http://www.microapl.co.uk/apl/index.html">APL Home</A> ]</center> <br></td> </tr> <tr> <td width="120"> <a href="http://www.microapl.co.uk/apl/index.html"><img height="68" border="0" width="119" src="MicroAPL_logo.gif" alt="www.microapl.co.uk"></a> </td> <td align="left" valign="bottom"> <h1>Interfacing to Ruby</h1> </td> </tr> <tr> <td width="800" valign="top" colspan="2"> <hr> <H2>Specifying a call to Ruby</H2> <p>You can interface to Ruby by supplying <tt>'ruby'</tt> as the environment string (left argument) for <A HREF="ch_020_070_505.htm"><code>⎕NEW</code></A>, <A HREF="ch_020_070_345.htm"><code>⎕GETCLASS</code></A>, or <A HREF="ch_020_070_095.htm"><code>⎕CALL</code></A>. These system functions will allow you to create an instance of a Ruby class, or to call a Ruby static mathod. </p> <H2>Loading modules and setting the Ruby DLL search path</H2> <p>Ruby classes are placed in <i>Modules</i>. When you use <code>⎕NEW</code> (or <code>⎕GETCLASS</code> or <code>⎕CALL</code>) to refer to a Ruby class, the system searches through the modules which have been loaded into the Ruby interpreter to resolve the class name. If the class is not found, you will get an error:</p> <pre> dt←'ruby' ⎕new 'DateTime' #<NameError: uninitialized constant DateTime> DOMAIN ERROR dt←'ruby' ⎕new 'DateTime' ^ </pre> <p>The above example has failed because <tt>DateTime</tt> is a class defined in the <tt>Date</tt> module, which has not been loaded. (In a Ruby script, you would get the same error if you had not loaded the <tt>Date</tt> module by using the Ruby <tt>'require'</tt> statement). You can tell the Ruby interpreter to load a module by using the system function <A HREF="ch_020_070_685.htm"><code>⎕SETUP</code></A> and the <tt>'require'</tt> keyword. For example:</P> <pre> 'ruby' ⎕SETUP 'require' 'Date' dt←'ruby' ⎕new 'DateTime' dt [ruby:DateTime] </pre> <P>The effect of using the <tt>'require'</tt> keyword is to add a given Ruby module (or script) to the list in which Ruby will search for class definitions. The parameter is a character vector containing the module name. This can be specified either as a full path name, or as just a file name, in which case Ruby will search in its current search path for the script: </p> <pre> 'ruby' ⎕SETUP 'require' 'c:\ruby\myapp.rb' </pre> <p>You can use the <tt>'addpath'</tt> keyword to add one or more directories to Ruby's current search path for modules: </p> <pre> 'ruby' ⎕SETUP 'addpath' 'c:\rubyapps' 'c:\rubylibs\version2' </pre> <p>See the documentation on <A HREF="ch_020_070_685.htm"><code>⎕SETUP</code></A> for more options for controlling the Ruby environment.</p> <H2>Conversion of Ruby data types to APL data</H2> <p>APLX by default applies the following data conversion rules to data returned from Ruby:</p> <ul> <li><p>Ruby integer ("Fixnum") values are converted to APL integers.</p></li> <li><p>Ruby Float and Bignum values are converted to APL floating-point numbers.</p></li> <li><p>Ruby Booleans (<tt>true</tt> or <tt>false</tt>) are converted to APL binary values 1 or 0</p></li> <li><p>Ruby Strings are converted to APL character arrays, translated to APLX internal representation.</p></li> <li><p>Ruby <tt>nil</tt> values are converted to APL Null objects</p> </li> <li><p>Simple Ruby arrays are converted to APL arrays, with individual elements converted as above. </p></li> </ul> <p>Anything else is left as an object in the Ruby environment, and a reference to the object is returned to APL.</p> <p>There are some special cases to consider. The data might not be convertible at all, or it might lose precision in the conversion. For example, a Ruby <tt>Bignum</tt> might have a higher precision than an APL double-precision floating point can represent. To handle cases like this, APLX provides the <A HREF="ch_020_080_140.htm"><code>⎕REF</code></A> system method. This forces the data to remain as a Ruby object. You can then call Ruby methods appropriate to the <tt>Bignum</tt> or other data type, to manipulate the data without losing precision.</p> <p>An example which cannot be represented at all is where a Ruby <tt>Double</tt> contains a NaN (Not A Number). APL does not handle NaNs, so it cannot be converted to an APL floating-point value. Instead, NaNs are left as Objects. If you try to use the data in an APL expression, you will get a DOMAIN ERROR, but you can see that it is a NaN and use Ruby methods on it.</p> <H2>Supplying Boolean arguments to Ruby methods</H2> <p>Ruby methods which expect <tt>true</tt> or <tt>false</tt> as arguments have to be handled in a special way, because Ruby does not allow 1 and 0 as equivalents to Booleans. To work around this, pass a one element matrix (<code>1 1⍴1</code> or <code>1 1⍴0</code>) as the argument; the interface will recognize this as a special case and convert the data to a Ruby <tt>true</tt> or <tt>false</tt> value. </p> <H2>Using the Ruby interface from multiple APL tasks</H2> <p>Because it is not safe to call the Ruby interpreter from multiple threads, you cannot use the Ruby interface from more than one APL task at a time. If you try to do so, you will get an error message and a FILE LOCKED error:</p> <pre> dt←'ruby' ⎕new 'Date' This interface cannot be used by more than one APL task at a time FILE LOCKED dt←'ruby' ⎕new 'Date' ^ </pre> <p>The lock will be cleared when the APL task which has been accessing Ruby executes a <tt>)CLEAR</tt>, <tt>)LOAD</tt>, or <tt>)OFF</tt>.</p> <H2>Evaluating Ruby expressions</H2> <p>Because Ruby is an interpreted language, it is possible to use <A HREF="ch_020_070_293.htm"><code>⎕EVAL</code></A> to run lines of Ruby code, and for setting up variables in the Ruby environment:</p> <pre> 'ruby' ⎕EVAL 's=String.new "Hello there"' Hello there 'ruby' ⎕EVAL 's.length' 11 'ruby' ⎕EVAL 'Math.sqrt(9)' 3 </pre> <H2>Ruby naming conventions</H2> <p>Ruby allows a method names to include various characters (such as <tt>=</tt> and <tt>?</tt>) which are not valid in APL names. Indeed by convention in Ruby, methods which return a Boolean often end with a question mark. For example, the method <tt>leap?</tt> of the Ruby <tt>DateTime</tt> class returns a Boolean indicating whether the date falls in a leap year, and the method <tt>responds_to?</tt> is a standard way of finding out whether a Ruby object supports a given method call (message).</p> <p>The problem with this is that the APL parser has a different view of what constitutes a valid name. So if you write:</p> <pre> is_leap_year←RUBYDATE.leap? </pre> <p>then the APL interpreter will think the rightmost token of the line is the APL ? primitive, separate from the compound identifier <tt>RUBYDATE.leap</tt>. It will therefore give an error.</p> <p>To work around this problem, the $ character can be used as an escape character in external names. It has the effect of treating the next character as part of the name. So the valid way of calling the <tt>is_leap?</tt> method is:</p> <pre> is_leap_year←RUBYDATE.leap$? </pre> <H3>Example 1</H3> <p>This example shows the use of the Ruby <tt>Hash</tt> class, which maintains a list of Key - Value pairs.</p> <pre> h←'ruby' ⎕NEW 'Hash' h.length 0 h.store 'France' 'Paris' Paris h.store 'UK' 'London' London h.store 'Italy' 'Rome' Rome h.store 'Germany' 'Berlin' Berlin h.length 4 h.to_a UK London France Paris Italy Rome Germany Berlin h.sort ⍝ Sort by key values, return array France Paris Germany Berlin Italy Rome UK London h.key$? 'France' ⍝ Does key 'France' exist? (Note use of $ escape character) 1 h.key$? 'USA' ⍝ Does key 'USA' exist? 0 h.fetch 'France' Paris h.fetch 'USA' #<IndexError: key not found> DOMAIN ERROR h.fetch 'USA' ^ </pre> <H3>Example 2</H3> <p>This example shows the use of the Ruby <tt>Complex</tt> class, for manipulating complex numbers:</p> <pre> 'ruby' ⎕setup 'require' 'complex' compclass←'ruby' ⎕GETCLASS 'Complex' a←⎕NEW compclass 3 4 a.⎕DS 3+4i b←⎕NEW compclass 2 ¯1 b.⎕DS 2-1i b.conjugate [ruby:Complex] b.conjugate.⎕DS 2+1i b.polar 2.236067977 ¯0.463647609 c←a.$* b ⍝ Complex multiplication. Note use of $ escape char c.⎕DS 10+5i </pre> <hr> </td> </tr> <tr> <td width="800" valign="top" colspan="2"> <center>Topic: <A HREF="ch.htm">APLX Help</A> : <A HREF="ch_070.htm">Interfacing to other languages</A> : <A HREF="ch_070_050.htm">Interfacing to Ruby</A> </center> <center> [<A HREF="ch_070_060.htm">Next</A> | <A HREF="ch_070_040.htm">Previous</A> | <A HREF="ch.htm">Contents</A> | <A HREF="help_index.htm">Index</A> | <A HREF="http://www.microapl.co.uk/apl/index.html">APL Home</A> ]</center> <br></td> </tr> </table> <!-- %%COMMON_BODY_TAIL%% --> <p class="copyright">Copyright © 1996-2010 MicroAPL Ltd</p> <!-- %%END%% --> </body> </html>