Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Name already taken #1

Open
wekempf opened this issue Jul 18, 2022 · 7 comments
Open

Name already taken #1

wekempf opened this issue Jul 18, 2022 · 7 comments

Comments

@wekempf
Copy link

wekempf commented Jul 18, 2022

There's already an FsSpec, a BDD testing framework for F#, that will cause problems and confusion. I suggest you find a better name?

I found this project via F# weekly and the blog post at https://spencerfarley.com/draft/fsspec/. It intrigued me immediately. Are you aware of clojure.spec? If not, there's lots to learn from there, as it's a very similar system that's been battle hardened. I very much like the idea of being able to specify constraints as data and will be watching this project with interest, and may even contribute. Sorry, I know this is commentary and not part of the issue, but I don't see any other way to interact yet.

@wekempf
Copy link
Author

wekempf commented Jul 18, 2022

Just noticed from docs that, yes, you do know about clojure.spec.

Thought I'd follow up with a name suggestion... fsharp.spec. In line with clojure.spec and has no ambiguity with FsSpec the testing library.

@farlee2121
Copy link
Owner

Glad the project intrigues you!

The library is currently usable and what it does is well covered with tests. Now the library needs use and feedback to feel out the rough edges in practical application.
I'd love to hear of your experiences if give the library a try!

As to the name, do you have a link to the other FsSpec library?

@wekempf
Copy link
Author

wekempf commented Jul 18, 2022

There's actually multiple GitHub repos with the name, but the one that matters is https://github.com/chaliy/fsspec which is a .NET/F# project. Granted, they don't have a NuGet package but I've seen the library referenced elsewhere. May not be the end of the world since they didn't create a package on nuget.org, but courtesy and avoiding confusion are reasons to at least consider it.

@farlee2121
Copy link
Owner

Hmm. That project doesn't have a NuGet package and hasn't been touched in 12 years.

I'll keep it in mind, but it doesn't feel pressing to make a change.
It's yet to be seen if this project will gain traction anyway.

I understand FSharp.spec, but I also hesitate because this library differs considerably from Clojure's spec system. There is no intent to take this library in the direction of set semantics or integrate it as part of the type system. Clojure.spec is built on predicates/expressions, which I considered and rejected as a sustainable approach for this library. Clojure.spec leans into Design-by-Contract-style assertions while this library embraces type-driven development.

@farlee2121
Copy link
Owner

This does have me thinking about how this library enables Clojure.spec-style constraint "inheritance".

One could build a core collection of common constraints or otherwise build up constraints in a readable way.

let markdown = //...
let sanitizedMarkdown = markdown &&& //...
let recipeIngredientSpec = sanitizedMarkdown &&& notEmpty

Do these semantic compositions of constraints need special representation? (maybe leaf type LabeledSpec (name*wrappedSpec)
It could improve message generation. It's not clear what other likely use it would serve... A very interesting line of thought

@wekempf
Copy link
Author

wekempf commented Jul 19, 2022

I'm a little confused by some of your clojure.spec descriptions. How is clojure.spec "part of the type system", especially when Clojure is a dynamic language? I'm also unsure what you mean by "Design-by-Contract-style assertions"? A constrained type is as much a "Design-by-Contract style assertion" as clojure.spec's are, and they are far more "part of the type system" than the wrappers added by clojure.spec. There are certainly differences between this and clojure.spec, where you're working within the type system of a strongly typed language and clojure.spec embraces the dynamic nature of the language it's working within. But in the end both are doing runtime type verification (even if, due to the nature of the strongly typed language, the runtime type verification here is confined only to data creation, rather than sprinkled everywhere). IMHO, neither is doing DbC, as you can't specify something like "when you pop from the stack the stack will have one less item" as you would in Eiffel's DbC.

@farlee2121
Copy link
Owner

Thanks for sharing your thoughts. I'm enjoying how this makes me think differently.

I'm not experienced in Eiffel, so I may not have the same understanding of DbC as you. But here are some of my reasonings

Clojure.spec as DbC -> Clojure specs are primarily for use in decorating functions with pre- and post-conditions (including relationships between input and output). These conditions will throw exceptions if violated, as with DbC I've experienced in other places.

  • FsSpec as type-driven -> FsSpec does do runtime validation, but the approach is very different. Errors are not asserted in each function and no exceptions are used. Instead, it leans into total signatures and types that require callers to deal with failed expectations up-front then not again. Associating specs to types isn't a required part of the library, but it is the approach the library is made to support.
  • I see how runtime validation is applied as a strong design compass with significant repercussions. Avoiding exceptions and distributed validation is a central motivation of this library. Other attempts have been made at DbC and exception-based method-level assertion for .NET. This library is not trying to serve that vein, though it can be used for such.

Clojure.spec is more a part of the type system -> F# certainly has a more rigorous type system and more types are leveraged to achieve a type-driven style. However, FsSpec is not essential to make any of that type-driven magic work. It can make it nicer, but all the type safety would be fine without it. Clojure, on the other hand, is dynamic as you've said. Spec is the language's approach to defining and enforcing types. I consider it similar to the optional type system TypeScript brings to Javascript or Sorbet to Ruby.

This does have me realizing how easy it would be to make descriptive assertions using spec data. Something like

let divide dividend divisor = 
  Spec.assert(NonNegativeInt, divisor)
  divident/divisor

The assertion can both check satisfaction and generate a descriptive exception from the spec. There are valid cases for this, even if I strongly prefer total functions over exceptions.

It's easy enough I think individual users could implement it if they want it, and we can integrate it if there is enough demand. Something like

module Spec = 
  let assert' spec value =
    let valueExplanation = Spec.explain spec value
    if Explanation.isOk valueExplanation.Explanation
    then ()
    else failwith (valueExplanation |> Formatters.prefix_allresults)

Maybe also make a custom exception type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants