pre-loaded module in ocaml, and their contents - module

Two questions:
How can I find what are the modules that are pre-loaded in my ocaml session?
Given a module, how can I list its contents?
In SML, a standard "trick" was to define a dummy module and open in it a module the contents of which I wanted to inspect. The signature of the dummy module would then list the contents of the module in SML. This does not work in ocaml.
Found this: To see the packages pre-loaded, use #use "topfind";; to use the topfind package, and the #list;; will list the pre-loaded packages!

I don't know a way to list the modules currently loaded.
To show the contents of a module:
# #show_module Pervasives;;
module Pervasives :
sig
external raise : exn -> 'a = "%raise"
external raise_notrace : exn -> 'a = "%raise_notrace"
val invalid_arg : string -> 'a
. . .
val unsafe_really_input : in_channel -> bytes -> int -> int -> unit
val do_at_exit : unit -> unit
end
The initial set of top-level directives is given in Section 9.2 of the OCaml manual.

Related

Reuse parent symbols in child module

I am seeking to re-use the same role/class names in a child module as in its parent. You know, like a specialization.
The aim is to be able to re-use the same script code for both the parent and child Series variants by simply changing use Dan to use Dan::Pandas at the top.
I am trying to stick to role rather than class compostion where I can so that the behaviours can be applied to other objects with eg. class GasBill does Series;
Here is the MRE:
me#ubuntu:~/spike# tree
.
├── lib
│   ├── Dan
│   │   └── Pandas.rakumod
│   └── Dan.rakumod
└── spike.raku
Dan.rakumod:
1 unit module Dan;
2
3 role Series is export {
4 method no { "no" }
5 }
Pandas.rakumod:
1 unit module Dan::Pandas;
2
3 use Dan;
4
5 role Series does Dan::Series is export {
6 method yo { "yo" }
7 }
spike.raku:
1 use lib './lib';
2 use Dan::Pandas;
3
4 my $s = Series.new; #version 1
5 #my $s = Dan::Pandas::Series.new; #version 2 "fqm"
6
7 say $s.no, $s.yo, $s.^name;
My version 1 says:
No appropriate parametric role variant available for 'Dan::Series':
Ambiguous call to '(Dan::Series)'; these signatures all match:
(::$?CLASS ::::?CLASS Mu $)
(::$?CLASS ::::?CLASS Mu $)
in block <unit> at spike.raku line 4
My version 2 says:
Could not find symbol '&Series' in 'Dan::Pandas'
in block <unit> at spike.raku line 5
I am trusting that raku does have a way to do this, but darned if I can work it out!
TL;DR You need a solution that avoids having two roles with the same full name.
Golf
role R {}
role R {}
R.new
displays:
No appropriate parametric role variant available for 'R':
Ambiguous call to '(R)'; these signatures all match:
(::$?CLASS ::::?CLASS Mu $)
(::$?CLASS ::::?CLASS Mu $)
(See Gut-referencing warning when composing stubbed role for a bit more discussion of the error message.)
A solution
Just Dan.
Dan.rakumod:
unit module Dan;
role Series is export { method no { "no" } }
spike.raku:
use lib '.';
use Dan;
my $s = Series.new;
.say for $s.?no, $s.?yo, $s.^name;
Running spike displays:
no
Nil
Dan::Series
Introduce Dan::Pandas.
Alter Dan so variant modules can use it without them importing Dan's unqualified Series symbol into their lexical scopes.
(Because otherwise Dan's unqualified Series symbol ends up being in their lexical scopes as well as their own unqualified Series symbol, and you get the "Ambiguous call" error.)
Dan:
unit module Dan;
role Series is export { method no { "no" } }
class Dan::Variant-Export-Dummy-Type is export(:Variant-Exports) {}
The change is the last line, a dummy declaration with a new export tag. (Introducing the new tag some cleaner way is left as an exercise for readers.)
Dan::Pandas:
unit module Dan::Pandas;
use Dan :Variant-Exports;
role Series does Dan::Series is export { method yo { "yo" } }
The change is using the new :Variant-Exports tag with the use Dan ... statement. This stops Dan's Series role being imported under that unqualified name into Dan::Pandas.
User programs will now work as expected/wanted.
Just change the use statement in spike.raku:
use lib '.';
use Dan::Pandas;
my $s = Series.new;
.say for $s.?no, $s.?yo, $s.^name;
Running with just this change for user programs:
no
yo
Dan::Pandas::Series
Note how there's no need for programs using using these modules to know about the :Variant-Exports tag. That's just an internal detail of the Dan module and the Dan::Pandas etc modules that lets them avoid the unintended role variant duplication problem shown at the start of this answer.
If I'm understanding correctly, you don't need/want to use the non-specialized role in the final module (that is, you aren't using the Series defined in Dan.rakumod in spike.raku – you're only using the specialized Series defined in Pandas.rakumod). Is that correct?
If so, the solution is simple: just don't export the Series from Dan.rakumod – it's still our scoped (the default for roles) so you can still use it in Pandas.rakumod exactly the way you currently do (Dan::Series). But, since it's not exported, you it won't create a name clash with the non-prefixed Series.
My Solution
Taking the advice of #codesections [that FQMs can be used without is export] and #raiph [that you can qualify the export with e.g. is export(:ALL)], then - yes - there is a neat way to do this.
viz. https://docs.raku.org/language/modules#Exporting_and_selective_importing
Turns out that this is low touch (just change two lines) and, as I originally hoped, something that raku designers have anticipated.
Here is the code from my OP adjusted...
Dan.rakumod:
1 unit module Dan;
2
3 role Series is export(:ALL) { #<== added the selective export
4 method no { "no" }
5 }
Pandas.rakumod (no change):
1 unit module Dan::Pandas;
2
3 use Dan;
4
5 role Series does Dan::Series is export {
6 method yo { "yo" }
7 }
spike.raku:
1 use lib './lib';
2 #use Dan::Pandas; #\ either of these work
3 use Dan :ALL; #/ <== use :ALL to get 'Series'
4
5 my $s = Series.new;
6
7 say $s.no, $s.yo, $s.^name; #No such method 'yo' if Pandas not used

Apache Jena - Is it possible to write to output the BASE directive?

I just started using Jena Apache, on their introduction they explain how to write out the created model. As input I'm using a Turtle syntax file containing some data about some OWL ontologies, and I'm using the #base directive to use relative URI's on the syntax:
#base <https://valbuena.com/ontology-test/> .
And then writing my data as:
<sensor/AD590/1> a sosa:Sensor ;
rdfs:label "AD590 #1 temperatue sensor"#en ;
sosa:observes <room/1#Temperature> ;
ssn:implements <MeasureRoomTempProcedure> .
Apache Jena is able to read that #base directive and expands the relative URI to its full version, but when I write it out Jena doesn't write the #base directive and the relative URI's. The output is shown as:
<https://valbuena.com/ontology-test/sensor/AD590/1> a sosa:Sensor ;
rdfs:label "AD590 #1 temperatue sensor"#en ;
sosa:observes <https://valbuena.com/ontology-test/room/1#Temperature> ;
ssn:implements <https://valbuena.com/ontology-test/MeasureRoomTempProcedure> .
My code is the following:
Model m = ModelFactory.createOntologyModel();
String base = "https://valbuena.com/ontology-test/";
InputStream in = FileManager.get().open("src/main/files/example.ttl");
if (in == null) {
System.out.println("file error");
return;
} else {
m.read(in, null, "TURTLE");
}
m.write(System.out, "TURTLE");
There are multiple read and write commands that take as parameter the base:
On the read() I've found that if on the data file the #base isn't declared it must be done on the read command, othwerwise it can be set to null.
On the write() the base parameter is optional, it doesn't matter if I specify the base (even like null or an URI) or not, the output is always the same, the #base doesn't appear and all realtive URI's are full URI's.
I'm not sure if this is a bug or it's just not possible.
First - consider using a prefix like ":" -- this is not the same as base but makes the output nice as well.
You can configure the base with (current version of Jena):
RDFWriter.create()
.source(model)
.lang(Lang.TTL)
.base("http://base/")
.output(System.out);
It seems that the command used on the introduction tutorial of Jena RDF API is not updated and they show the reading method I showed before (FileManager) which now is replaced by RDFDataMgr. The FileManager way doesn't work with "base" directive well.
After experimenting I've found that the base directive works well with:
Model model = ModelFactory.createDefaultModel();
RDFDataMgr.read(model,"src/main/files/example.ttl");
model.write(System.out, "TURTLE", base);
or
Model model = ModelFactory.createDefaultModel();
model.read("src/main/files/example.ttl");
model.write(System.out, "TURTLE", base);
Although the model.write() command is said to be legacy on RDF output documentation (whereas model.read() is considered common on RDF input documentation, don't understand why), it is the only one I have found that allows the "base" parameter (required to put the #base directive on the output again), RDFDataMgr write methods don't include it.
Thanks to #AndyS for providing a simpler way to read the data, which led to fix the problem.
#AndyS's answer allowed me to write relative URIs to the file, but did not include the base in use for RDFXML variations. To get the xml base directive added correctly, I had to use the following
RDFDataMgr.read(graph, is, Lang.RDFXML);
Map<String, Object> properties = new HashMap<>();
properties.put("xmlbase", "http://example#");
Context cxt = new Context();
cxt.set(SysRIOT.sysRdfWriterProperties, properties);
RDFWriter.create().source(graph).format(RDFFormat.RDFXML_PLAIN).base("http://example#").context(cxt).output(os);

How do I get distinct items from a list?

It's not clear to me how to retrieve distinct items from a list.
I have the following code:
topicsFromLinks : List Link -> List Topic
topicsFromLinks links =
links
|> List.map (\l -> l.topics)
|> List.concat
|> Set.fromList
|> Set.toList
Error:
The definition of topicsFromLinks does not match its type
annotation. - The type annotation for topicsFromLinks says it always
returns:
List Topic
But the returned value (shown above) is a:
List comparable
I expect the following lines to just work in regards to structural equality:
|> Set.fromList
|> Set.toList
Why am I receiving a List of Comparables?
How do I resolve this compilation error?
Appendix:
type alias Topic =
{ name : String, isFeatured : Bool }
type alias Link =
{
...
, topics : List Topic
...
}
According to the documentation for Set:
The values can be any comparable type. This includes Int, Float, Time, Char, String, and tuples or lists of comparable types.
You are attempting to put a Topic value in where only a comparable type will work.
Thankfully there is the elm-community/list-extra package which exposes a uniqueBy function that lets you specify your own function to turn something into a comparable.
If you want to get the distinct list of topics, matching both on the name and isFeatured fields, then you can use toString to serialize the value into something comparable:
import List.Extra exposing (uniqueBy)
topicsFromLinks : List Link -> List Topic
topicsFromLinks links =
links
|> List.map .topics
|> List.concat
|> uniqueBy toString
In 2021, rather than using an external library and writing a clumsy toString method (since Debug.toString isn't available for production use anymore), consider using a fold:
unique : List a -> List a
unique list =
List.foldl
(\a uniques ->
if List.member a uniques then
uniques
else
uniques ++ [ a ]
)
[]
list

How to define function with record argument in erlang

I want to define the structure of my function input.
This module defines my function (part of it is pseudo code):
-module(my_service).
-export([my_function/2]).
-type my_enum() :: 1 | 2 | 5.
-record(my_record, {id = null :: string(), name = null :: string(), myType = null :: my_enum()}).
-spec my_function(MyValue#my_record) -> Result#my_record.
my_function(ClientRequest, MyValue) when is_record(Entry, my_record) ->
Response = client:request(get, "http://www.example.com/api?value=" + MyValue#my_record.name),
case Response of
{ok, Status, Head, Body} ->
Result = parse_response(Body, my_record);
Error ->
Error
end.
This is how I want to call it:
-module(test1).
-import(io, [format/2]).
main(_) ->
application:start(inets),
MyTestValue = #my_record{name => "test value", myType => 2},
X = my_service:my_function(MyTestValue),
io:format("Response: ~p", [X]).
So, any idea how I can force the type of the function input to be of type my_record?
It's often handy to destructure a record directly in the function argument list, which also forces that argument to have the desired record type. For example, we can slightly modify your my_function/2 like this:
my_function(ClientRequest, #my_record{name=Name}=MyValue) ->
Response = client:request(get, "http://www.example.com/api?value=" ++ Name),
case Response of
{ok, Status, Head, Body} ->
Result = parse_response(Body, MyValue);
Error ->
Error
end.
Note how we're pattern-matching the second parameter MyValue: we're not only asserting that it's a #my_record{} instance, but we're also extracting its name field into the Name variable at the same time. This is handy because we use Name on the first line of the function body. Note that we also keep the parameter name MyValue because we pass it to parse_response/2 within the function body. If we didn't need it, we could instead write the function head like this:
my_function(ClientRequest, #my_record{name=Name}) ->
This would still have the desired effect of forcing the second argument to be of type #my_record{}, and would also still extract Name. If desired, you could extract other fields in a similar manner:
my_function(ClientRequest, #my_record{name=Name, id=Id}) ->
Both Name and Id are then available for use within the function body.
One other thing: don't use the atom null as a default for your record fields. The default for Erlang record fields is the atom undefined, so you should just use that. This is preferable to defining your own concept of null in the language.

Passing options to JS function with Amber

I'm trying to write the equivalent of:
$( "#draggable" ).draggable({ axis: "y" });
in Amber smalltalk.
My guess was: '#draggable' asJQuery draggable: {'axis' -> 'y'} but that's not it.
Not working on vanilla 0.9.1, but working on master at least last two months ago is:
'#draggable' asJQuery draggable: #{'axis' -> 'y'}
and afaict this is the recommended way.
P.S.: #{ 'key' -> val. 'key2' -> val } is the syntax for inline creation of HashedCollection, which is implemented (from the aforementioned two-month ago fix) so that only public (aka enumerable) properties are the HashedCollection keys. Before the fix also all the methods were enumerable, which prevented to use it naturally in place of JavaScript objects.
herby's excellent answer points out the recommended way to do it. Appearently, there is now Dictionary-literal support (see his comment below). Didn't know that :-)
Old / Alternate way of doing it
For historical reasons, or for users not using the latest master version, this is an alternative way to do it:
options := <{}>.
options at: #axis put: 'y'.
'#draggable' asJQuery draggable: options.
The first line constructs an empty JavaScript object (it's really an JSObjectProxy).
The second line puts the string "y" in the slot "axis". It has the same effect as:
options.axis = "y"; // JavaScript
Lastly, it is invoked, and passed as a parameter.
Array-literals vs Dictionaries
What you were doing didn't work because in modern Smalltalk (Pharo/Squeak/Amber) the curly-brackets are used for array-literals, not as an object-literal as they are used in JavaScript.
If you evaluate (print-it) this in a Workspace:
{ #elelemt1. #element2. #element3 }.
You get:
a Array (#elelemt1 #element2 #element3)
As a result, if you have something that looks like a JavaScript object-literal in reality it is an Array of Association(s). To illustrate I give you this snippet, with the results of print-it on the right:
arrayLookingLikeObject := { #key1 -> #value1. #key2 -> #value2. #key3 -> #value3}.
arrayLookingLikeObject class. "==> Array"
arrayLookingLikeObject first class. "==> Association"
arrayLookingLikeObject "==> a Array (a Association a Association a Association)"
I wrote about it here:
http://smalltalkreloaded.blogspot.co.at/2012/04/javascript-objects-back-and-forth.html