caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] [ANN] slacko-0.9.0
@ 2014-10-04 13:05 Marek Kubica
  2014-10-04 13:36 ` Malcolm Matalka
  0 siblings, 1 reply; 6+ messages in thread
From: Marek Kubica @ 2014-10-04 13:05 UTC (permalink / raw)
  To: Caml List

[-- Attachment #1: Type: text/plain, Size: 535 bytes --]

Hello,

I'd like to announce the first public release of Slacko, a REST API
binding to Slack <https://slack.com/>. I'm looking for input on it, so
I can publish a 1.0 soon.

The source code can be found on GitHub,
<https://github.com/Leonidas-from-XIV/slacko> and it is already
uploaded in OPAM <http://opam.ocaml.org/packages/slacko/slacko.0.9.0/>.

More information can be found on my blog,
<http://xivilization.net/~marek/blog/2014/10/01/introducing-slacko/>.

Hope it will prove useful to someone.

regards,
Marek

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Caml-list] [ANN] slacko-0.9.0
  2014-10-04 13:05 [Caml-list] [ANN] slacko-0.9.0 Marek Kubica
@ 2014-10-04 13:36 ` Malcolm Matalka
  2014-10-04 14:54   ` Marek Kubica
  0 siblings, 1 reply; 6+ messages in thread
From: Malcolm Matalka @ 2014-10-04 13:36 UTC (permalink / raw)
  To: Marek Kubica; +Cc: Caml List

I would either change the 'apierror' type to normal variants, or spread
out the apierror to multiple types.  Can every API function really
return every one of those errors?  The value of polymorphic variants is
they are module, and you can share the constructors across types.

I would also structure the API calls in terms of the Result.t type in
Core.  Right now success is part of the error type.

My 2 cents.

Marek Kubica <marek@xivilization.net> writes:

> Hello,
>
> I'd like to announce the first public release of Slacko, a REST API
> binding to Slack <https://slack.com/>. I'm looking for input on it, so
> I can publish a 1.0 soon.
>
> The source code can be found on GitHub,
> <https://github.com/Leonidas-from-XIV/slacko> and it is already
> uploaded in OPAM <http://opam.ocaml.org/packages/slacko/slacko.0.9.0/>.
>
> More information can be found on my blog,
> <http://xivilization.net/~marek/blog/2014/10/01/introducing-slacko/>.
>
> Hope it will prove useful to someone.
>
> regards,
> Marek

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Caml-list] [ANN] slacko-0.9.0
  2014-10-04 13:36 ` Malcolm Matalka
@ 2014-10-04 14:54   ` Marek Kubica
  2014-10-04 15:23     ` Gabriel Scherer
  0 siblings, 1 reply; 6+ messages in thread
From: Marek Kubica @ 2014-10-04 14:54 UTC (permalink / raw)
  To: Malcolm Matalka; +Cc: Caml List

Hello Malcolm,

Thanks for looking into my code.

On Sat, 04 Oct 2014 13:36:43 +0000
Malcolm Matalka <mmatalka@gmail.com> wrote:

> I would either change the 'apierror' type to normal variants, or
> spread out the apierror to multiple types.  Can every API function
> really return every one of those errors?  The value of polymorphic
> variants is they are module, and you can share the constructors
> across types.

You're right, there is a common set of errors that most functions return
and then there are some specific errors.

Originally I didn't want to create such a huge type but have every
function have its own polymorphic variants that apply to it, so users
of the API know exactly what errors might be thrown and have to handle
these. The huge type is a direct result of the "validate" function
<https://github.com/Leonidas-from-XIV/slacko/blob/bc6be47cca8d2088d3d7cfb9af0603a350ab0215/src/slacko.ml#L79>
which tests all cases.

I tried specifying a smaller subset of polymorphic variants, but the
compiler always selected the full set. I don't know how to fix this,
because on the one hand side I don't want to have a big monolithic
error type but on the other hand, I don't want to duplicate a subset of
the validate function in every function.

Looking forward for ideas on how to solve this in a more elegant way.

> I would also structure the API calls in terms of the Result.t type in
> Core.  Right now success is part of the error type.

If I understand you correctly, this should do the trick with the
current code:

type result = Success of Yojson.Basic.json | Error of apierror

For future releases I plan to replace the strings in the signatures
with proper types (so, some "string" turn into a "token" types, others
into timestamp types) and to process the resulting JSON into some more
convenient data structures. But for now, I hope to get the error
handling right and then build upon this.

Thanks in advance!

regards,
Marek

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Caml-list] [ANN] slacko-0.9.0
  2014-10-04 14:54   ` Marek Kubica
@ 2014-10-04 15:23     ` Gabriel Scherer
  2014-10-04 22:43       ` Marek Kubica
  0 siblings, 1 reply; 6+ messages in thread
From: Gabriel Scherer @ 2014-10-04 15:23 UTC (permalink / raw)
  To: Marek Kubica; +Cc: Malcolm Matalka, Caml List

Re. the "validate" function, you can do it using the ability to
pattern-match on a named subset of the polymorphic variants.

Let's consider a simplification of your problem with only two classes
of errors (taken from your code):

  type error_cant_kick = [
  | `Cant_kick_from_general
  | `Cant_kick_from_last_channel
  | `Cant_kick_self
  ]

  type error_cant_invite = [
  | `Cant_invite
  | `Cant_invite_self
  ]

  type 'a error = [
  | `Success of 'a
  | error_cant_kick
  | error_cant_invite
  ]

The `validate` function indeed processes all cases, returning an
everything-possible error type:

  let validate : _ -> _ error = function
    | `String "cant_invite_self" -> `Cant_invite_self
    | `String "cant_invite" -> `Cant_invite
    | `String "cant_kick_self" -> `Cant_kick_self
    | `String "cant_kick_from_general" -> `Cant_kick_from_general
    | `String "cant_kick_from_last_channel" -> `Cant_kick_from_last_channel

But you can refine its result if you know (from the informal
API specification) that it can only be some subset, using the #foo
syntax. In the example below, `kick` and `invite` have been refined
with distinct subsets:

  let api_mismatch err =
    failwith ("API mismatch: unexpected error " (*^ to_string err *))

  let kick f =
    f ()
    |> validate
    |> function
      | `Success v -> `Success v
      | #error_cant_kick as err -> err
      | other -> api_mismatch other

  let invite f =
    f ()
    |> validate
    |> function
      | `Success v -> `Success v
      | #error_cant_invite as err -> err
      | other -> api_mismatch other

`kick` now returns a [> error_cant_kick | `Success of 'a ], while `invite`
returns [> error_cant_invite | `Success of 'a ].

On Sat, Oct 4, 2014 at 4:54 PM, Marek Kubica <marek@xivilization.net> wrote:
> Hello Malcolm,
>
> Thanks for looking into my code.
>
> On Sat, 04 Oct 2014 13:36:43 +0000
> Malcolm Matalka <mmatalka@gmail.com> wrote:
>
>> I would either change the 'apierror' type to normal variants, or
>> spread out the apierror to multiple types.  Can every API function
>> really return every one of those errors?  The value of polymorphic
>> variants is they are module, and you can share the constructors
>> across types.
>
> You're right, there is a common set of errors that most functions return
> and then there are some specific errors.
>
> Originally I didn't want to create such a huge type but have every
> function have its own polymorphic variants that apply to it, so users
> of the API know exactly what errors might be thrown and have to handle
> these. The huge type is a direct result of the "validate" function
> <https://github.com/Leonidas-from-XIV/slacko/blob/bc6be47cca8d2088d3d7cfb9af0603a350ab0215/src/slacko.ml#L79>
> which tests all cases.
>
> I tried specifying a smaller subset of polymorphic variants, but the
> compiler always selected the full set. I don't know how to fix this,
> because on the one hand side I don't want to have a big monolithic
> error type but on the other hand, I don't want to duplicate a subset of
> the validate function in every function.
>
> Looking forward for ideas on how to solve this in a more elegant way.
>
>> I would also structure the API calls in terms of the Result.t type in
>> Core.  Right now success is part of the error type.
>
> If I understand you correctly, this should do the trick with the
> current code:
>
> type result = Success of Yojson.Basic.json | Error of apierror
>
> For future releases I plan to replace the strings in the signatures
> with proper types (so, some "string" turn into a "token" types, others
> into timestamp types) and to process the resulting JSON into some more
> convenient data structures. But for now, I hope to get the error
> handling right and then build upon this.
>
> Thanks in advance!
>
> regards,
> Marek
>
> --
> Caml-list mailing list.  Subscription management and archives:
> https://sympa.inria.fr/sympa/arc/caml-list
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Caml-list] [ANN] slacko-0.9.0
  2014-10-04 15:23     ` Gabriel Scherer
@ 2014-10-04 22:43       ` Marek Kubica
  2014-10-05  2:23         ` Jacques Garrigue
  0 siblings, 1 reply; 6+ messages in thread
From: Marek Kubica @ 2014-10-04 22:43 UTC (permalink / raw)
  To: Gabriel Scherer; +Cc: Malcolm Matalka, Caml List

[-- Attachment #1: Type: text/plain, Size: 1096 bytes --]

Hello Gabriel,

On Sat, 4 Oct 2014 17:23:02 +0200
Gabriel Scherer <gabriel.scherer@gmail.com> wrote:

> `kick` now returns a [> error_cant_kick | `Success of 'a ], while
> `invite` returns [> error_cant_invite | `Success of 'a ].

Thank you for this idea, I was actually looking for a way to define
subsets of polymorphic variants, I didn't know that I can just put them
into other types and was unaware of the # syntax. Now my functions
return just the possible error variants, not all of them anymore.

I've reworked the code to get rid of the huge apierror type (which I
suppose I can throw away completely, since a user of the API will never
need it anyway), but it lead to quite huge blow-up of signatures:

<https://github.com/Leonidas-from-XIV/slacko/blob/37c3626bb9574bc99325267fb4c9f9c3c4f4730c/src/slacko.mli>

I am going to simplify these subsets a bit (since e.g. all history
methods use the same polymorphic variants and some possible error types
imply other error types) but it kinda looks very verbose now - any hints
on what can be done?

regards,
Marek

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Caml-list] [ANN] slacko-0.9.0
  2014-10-04 22:43       ` Marek Kubica
@ 2014-10-05  2:23         ` Jacques Garrigue
  0 siblings, 0 replies; 6+ messages in thread
From: Jacques Garrigue @ 2014-10-05  2:23 UTC (permalink / raw)
  To: Marek Kubica; +Cc: OCaML List Mailing

On 2014/10/05 07:43, Marek Kubica wrote:
> 
> Hello Gabriel,
> 
> On Sat, 4 Oct 2014 17:23:02 +0200
> Gabriel Scherer <gabriel.scherer@gmail.com> wrote:
> 
>> `kick` now returns a [> error_cant_kick | `Success of 'a ], while
>> `invite` returns [> error_cant_invite | `Success of 'a ].
> 
> Thank you for this idea, I was actually looking for a way to define
> subsets of polymorphic variants, I didn't know that I can just put them
> into other types and was unaware of the # syntax. Now my functions
> return just the possible error variants, not all of them anymore.
> 
> I've reworked the code to get rid of the huge apierror type (which I
> suppose I can throw away completely, since a user of the API will never
> need it anyway), but it lead to quite huge blow-up of signatures:
> 
> <https://github.com/Leonidas-from-XIV/slacko/blob/37c3626bb9574bc99325267fb4c9f9c3c4f4730c/src/slacko.mli>
> 
> I am going to simplify these subsets a bit (since e.g. all history
> methods use the same polymorphic variants and some possible error types
> imply other error types) but it kinda looks very verbose now - any hints
> on what can be done?

I don’t think it’s particularly verbose.
You have documented all the possible errors for each function, which is
valuable information, and it would take the same space (or more) if you
were to write this in comments.
If you think this is hard to read, you can try some factorization, but I’m not
sure it would help. Also, from a stylistic point of view I would write variant
definitions on a single line, since there are never more than 3 cases.

This said, there is always a tension between safety an complexity.
If in practice static checking of possible errors doesn’t matter that much,
it would be easier to write this code using normal variants.

Jacques Garrigue

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2014-10-05  2:23 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-10-04 13:05 [Caml-list] [ANN] slacko-0.9.0 Marek Kubica
2014-10-04 13:36 ` Malcolm Matalka
2014-10-04 14:54   ` Marek Kubica
2014-10-04 15:23     ` Gabriel Scherer
2014-10-04 22:43       ` Marek Kubica
2014-10-05  2:23         ` Jacques Garrigue

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).