What are the requirements for position of custom attributes in an Erlang module? - erlang

It seems:
Dialyzer and erlc both error when there is a user-defined attribute in a source file before -module
Shell session demonstrating:
~ cat sample.erl
-my_attr(my_value).
-module(sample).
-compile([export_all, nowarn_export_all]).
main(_) ->
ok.
~ erlc sample.erl
sample.erl:1:2: no module definition
% 1| -my_attr(my_value).
% | ^
~ dialyzer sample.erl
Checking whether the PLT /Users/mheiber/Library/Caches/erlang/.dialyzer_plt is up-to-date... yes
Proceeding with analysis...
dialyzer: Analysis failed with error:
Could not scan the following file(s):
/Users/mheiber/sample.erl:1:2: no module definition
Last messages in the log cache:
Reading files and computing callgraph...
Is the contract for order of user-defined attributes documented anywhere? Or is this a bug?
All I could find in the docs is that predefined attributes must come before function declarations:
https://www.erlang.org/doc/reference_manual/modules.html#pre-defined-module-attributes

-module(Module).
Module declaration, defining the name of the module. The name Module,
an atom, is to be same as the file name minus the extension .erl.
Otherwise code loading does not work as intended.
This attribute is to be specified first and is the only mandatory attribute.

Related

Using records in functions

I was just playing with records, and am stuck trying to write a function that works with records. I think the problem is how to read the record definition into my module file.
The reocords.hrl file:
% Modeling a todo list
-record(todo, {status=reminder, who=joe, text}).
And then the use_records.erl file:
%% Use the records defined in "records.hrl"
-module(use_records).
-export([todo_to_tuple/1]).
rr("records.hrl").
todo_to_tuple(#todo{who=W, text=T} = R) -> {W, T}.
When I try to compile it, I get:
24> c(use_records).
use_records.erl:5: variable 'T' is unbound
use_records.erl:5: variable 'W' is unbound
use_records.erl:5: record todo undefined
use_records.erl:5: Warning: variable 'R' is unused
error
The error is the same if I remove the rr("records.hrl") line. So I suppose the real issue is being able to read the record definition (or not?). Please help!
rr/1 is used include record definition only in the shell.
In order to include record definition in your code use:
-include("records.hrl")

Compiling with ocamlbuild and camlp5

I am trying to make a project I am working on compile with ocamlbuild, in order to avoid the use of a regular Makefile, which I find to be slightly more complicated.
Specifically, I have a syntax extension file (extend.ml), which I need to be compiled first. In a regular Makefile there would be a rule:
extend.cmo: extend.ml
$(OCAMLC) -pp "camlp5o pa_extend.cmo q_MLast.cmo" -I +camlp5 -c $<
and then, for calculating the dependencies there would be a rule like this:
depend: $(MLFILES) extend.cmo
$(OCAMLDEP) -pp "camlp5o ./extend.cmo"
Of course, the creation of any object file would require a similar rule to the one above.
My question is, how can I integrate these rules/requirements to one ocamlbuild command (if possible)?
I have tried to compile the extend.ml file first, and then use the following command:
ocamlbuild -pp "camlp5o ./extend.cmo" -I +camlp5 -use-menhir -no-hygiene Main.byte
but I don't think it's optimal in any way.
Unfortunately, I am not familiar with the use of ocamlbuild as a compilation tool, so any help will be much appreciated.
You could define two new tags, compile_extend and use_extend, that specify the expected options. In your myocamlbuild.ml file:
open Ocamlbuild_plugin
let my_flags () =
flag ["ocaml"; "pp"; "compile_extend"]
(S [A"camlp5o"; A "pa_extend.cmo"; A "q_MLast.cmo"]);
flag ["ocaml"; "pp"; "use_extend"]
(S [A"camlp5o"; A "extend.cmo"]);
(* files with the use_extend flag must depend on extend.cmo *)
dep ["ocaml"; "use_extend"] ["extend.cmo"];
()
let () =
dispatch (function
| After_rules ->
my_flags ();
| _ -> ())
Then you would have a tags file with:
"extend.cmo": compile_extend
<Main.*>: use_extend
That said, this is all bling guessing, I have not tested this setup. Could you provide a tarball with an example extend.ml file and Main.ml allowing to reproduce your situation?

Compile with standalone flag gives compilation errors in client code

I'm attempting to compile Zero29 with the --standalone compiler flag. The project itself compiles fine, but I have a unit test project that exercises some code in the Zero29 project, even though it's an executable program (.exe).
Everything works fine without the --standalone compilation flag.
However, when I add the --standalone compilation flag to the Zero29 project, the Zero29 project compiles fine, but in the unit test project, the compiler complains about this Discriminated Union defined in the Zero29 project:
namespace Ploeh.ZeroToNine
open System
open Ploeh.ZeroToNine.Versioning
type Arg =
| Assign of Version
| AssignRank of Rank * int
| Increment of Rank
| ListVersions
| ShowHelp
| Unknown of string list
The unit test project directly references the Zero29 project:
Zero29.UnitTests --references--> Zero29 (where --standalone is added)
When I attempt to compile the entire solution, the Zero29 project compiles with the --standalone flag, but then compilation of Zero29.UnitTests fails. There are several errors, but they are all the same, so here's a single example:
error FS0039: The value or constructor 'Assign' is not defined
Which points to the third line of this code:
let ParseAssignVersionReturnsCorrectResult(version : string) =
let actual = [| "-a"; version |] |> Args.Parse
verify <# [Assign(Version version)] = (actual |> Seq.toList) #>
The strange thing is that while the compiler complains about Assign in the third line of this code snippet, it doesn't complain about the use of Args.Parse, even though it's defined in the same code file as the Arg Discriminated Union.
Why does it do that, and how can I resolve this issue?
(I've attempted to distil the problem here, but the links I've provided point to the actual code files on GitHub, if more information is required.)
Libraries compiled with the --standalone switch cannot expose any F# datatypes. This is, for one, expressly stated in Pickering (2007), p. 210. In your case, a discriminated union is one of these prohibited types. The fact that the file is an executable changes nothing here: it becomes a library the moment you attempt to use it as one.
There have been also multiple reports (for example, here and here) that even libraries compiled with --standalone behave, quoting one of these sources, “funky.” It would be safe to say that the use of this switch should perhaps be limited to stand-alone executables only (and they cannot pretend to be a library even when under unit tests).
Pickering R. (2007). Foundations of F#. Apress.

Erlang binary: Will this result in a copy?

After reading this document, I'm not sure whether or not the following code will make the run-time copy binaries of A internally.
f(<<>>, A) ->
A;
f(<<X:2/binary, R/binary>>, A) ->
I = binary_to_integer(X, 16),
f(R, <<A/binary, I>>).
My guess is "no", because A isn't sent nor is it deconstructed. Am I right, or did I miss something?
Your code will not result in run-time copying of the A binaries since no sharing of sub-binaries is done. If we manually unroll the sequence of append operations it looks something like
A0 = <<A/binary, SomeByte>>,
A1 = <<A0/binary, SomeByte1>>,
A2 = <<A1/binary, SomeByte2>>,
.....
An = <<An-1, SomeByteN>>.
So we are only appending to the binary resulting from the latest append operation, i.e. there is a single reference to the ProcBin that was created for A0(as described in the efficiency documentation).
Why not use bin_opt_info option ?
For your code :
[root#nimbus bin_test]# erlc +bin_opt_info a.erl
a.erl:8: Warning: OPTIMIZED: creation of sub binary delayed
For the code in https://gist.github.com/wardbekker/5673200
The output is :
[root#nimbus bin_test]# erlc +bin_opt_info test.erl
test.erl:14: Warning: OPTIMIZED: creation of sub binary delayed
[root#nimbus bin_test]# erlc +bin_opt_info test2.erl
test2.erl:8: Warning: variable 'A' is unused
test2.erl:13: Warning: OPTIMIZED: creation of sub binary delayed

Defining Modules VS.NET vs F# Interactive

I have written this code which compiles and works perfectly in VS.NET 2010
module ConfigHandler
open System
open System.Xml
open System.Configuration
let GetConnectionString (key : string) =
ConfigurationManager.ConnectionStrings.Item(key).ConnectionString
however when I do a control + A and Alt + Enter to send this to FSI I get an error
ConfigHandler.fs(2,1): error FS0010: Unexpected start of structured construct in definition. Expected '=' or other token.
OK.
So I change my code to
module ConfigHandler =
open System
open System.Xml
open System.Configuration
let GetConnectionString (key : string) =
ConfigurationManager.ConnectionStrings.Item(key).ConnectionString
Now Control + A, Alt + Enter is successful and I FSI nicely tells me
module ConfigHandler = begin
val GetConnectionString : string -> string
end
However now If I try to compile my code in VS.NET 2010, I get an error message
Files in libraries or multiple-file applications must begin with a namespace or module declaration, e.g. 'namespace SomeNamespace.SubNamespace' or 'module SomeNamespace.SomeModule'
How can I have both? Ability to compile in VS.NET and the ability to send modules to FSI?
There is a tiny -- but crucial -- difference between your two snippets of code which is to blame here.
F# has two ways to declare a module. The first, a "top-level module", is declared like this:
module MyModule
// ... code goes here
The other way to declare a module is as a "local module", like so:
module MyModule =
// ... code goes here
The main differences between the "top-level" and "local" declarations are that the local declaration is followed by an = sign and the code within a "local" module must be indented.
The reason you get the ConfigHandler.fs(2,1): error FS0010: Unexpected start of structured construct in definition. Expected '=' or other token. message for the first snippet is that you can't declare top-level modules in fsi.
When you added the = sign to your module definition, it changed from a top-level module to a local module. From there, you got the error Files in libraries or multiple-file applications must begin with a namespace or module declaration, e.g. 'namespace SomeNamespace.SubNamespace' or 'module SomeNamespace.SomeModule' because local modules must be nested within a top-level module or a namespace. fsi doesn't allow you to define namespaces (or top-level modules), so if you want to copy-paste the entire file into fsi the only way it'll work is if you use the compilation directives as #pad mentioned. Otherwise, you can simply copy-paste the local module definitions (without the containing namespace) into fsi and they should work as expected.
Reference:
Modules (F#) on MSDN
The common solution is to keep the first example and create a fsx file which references the module:
#load "ConfigHandler.fs"
You have advantage of loading multiple modules and writing plumbing code for experiment.
If you really want to load ConfigHandler.fs directly to F# Interactive, you can use INTERACTIVE symbol and compiler directives:
#if INTERACTIVE
#else
module ConfigHandler
#endif
which works for both fsi and fsc.

Resources