[erlang-questions] -spec Enhancement: -spec f :: ftype().

Tim Watson <>
Tue Feb 14 20:40:39 CET 2012


On 14 February 2012 16:14, David Mercer <> wrote:

> You know, I’ve always wished I could declare a function type (e.g., -type
> f() :: fun((arg_type1(), arg_type2()) -> return_type())) and then declare
> a function as conforming to that spec (e.g., -spec f1 :: f()), instead of
> having to repeat myself (i.e., -spec f1(arg_type1(), arg_type2()) ->
> return_type()).  This helps when the function type is quite complicated
> and needs to be repeated often, and also when you have a function that
> takes a function with a certain spec, when you define the actual functions
> that can be passed to it.  What do y’all think?  Am I crazy?****
>
> ** **
>
>
I wonder if, combined with custom behaviours, that might be a step in the
direction of being able to provide a consistent interface to modules that
offer equivalent functionality (database connectivity, config file
handling, logging, etc). I'm assuming that is the primary use case,
although I have a feeling you have encountered other scenarios from what
you've said.

I think it sounds quite handy, although presumably you'd want to export the
type from a module somewhere, in which case it gets scoped to an
application and it *could* get hairy trying to figure out the right place
to put it. Pushing it down to the appropriate scope would then make it less
appropriate to re-use across modules/components.

For example, consider a concrete example specification:

    -opaque dbms_connection() :: #dbms_connection_handle{}.  %% with some
fields for vendor specific stuff
    -type dbms_connection_info() :: #dbms_connection_info{}.  %% with some
fields for vendor specific stuff
    -type open_connection(connection_info()) -> dbms_connection() | {error,
term()}.

Where should I export this from? Do I have some 'core' module (say, the
primary or application callback module for the app/library) where I do
this?

    %% in dbms.erl
    -include("dbms.hrl").
    -export_type([open_connection/1]).

And then presumably I'm going to use this in a few different places. It'd
be fine, I suppose, to see something like

    -spec open_connection() :: dbms:open_connection()
    open_connection(CInfo) -> .....

But then if I start to use that across multiple applications, does it still
make sense?

    -module(pgsql_connection).
    -spec open_connection() :: dbms:open_connection()
    open_connection(CInfo) -> .....

I suppose that reads ok, but why not just put it into a header file as a
sepc and distribute that? If that's the case, I can have a library
application carry the header and just use -spec instead. Any implementor
can simply include the header and reference the spec:

    %% in application/library dbms_api/include/dbms_api.hrl
    -opaque dbms_connection() :: #dbms_connection_handle{}.  %% with some
fields for vendor specific stuff
    -type dbms_connection_info() :: #dbms_connection_info{}.  %% with some
fields for vendor specific stuff
    -spec open_connection(connection_info()) -> dbms_connection() | {error,
term()}.

    %% in application dbms_pgsql/pgsql.erl

    -include_lib("dbms_api/include/dbms_api.hrl").

    %% we 'inherit' the -spec from our library dependency on dbms_api
    open_connection(CInfo) ->
        dbms_pgsql_connection:open(CInfo).


So here I'm not sure what the value is. If we're thinking about three or
four functions that have the same signature in terms of input arguments and
return type, but have different names, then Kostis correct me if I'm wrong
but I think it'd be pretty tough to do something like that because the
function name *is* part of the signature.

You *could* experiment with this idea by writing a parse_transform that
creates the -spec in response to a user defined annotation (
https://github.com/hyperthunk/annotations) but I'm not sure if (a) the
-type and -spec data will appear during the parse_transform execution or
(b) how you export a -spec from a parse_transform, or if this (b) is even
possible.

Another approach might be to write a build plugin (for rebar or whatever)
that reads a bunch of metadata and generates the specs accordingly. Yet
another approach might be to provide a hint to TypEr and let it generate
the -specs for you.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20120214/9b342407/attachment.html>


More information about the erlang-questions mailing list