robstoll / atrium-roadmap

Contains issues created by the maintainers of Atrium

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Avoid Duplications Between Infix and Fluent API

jGleitz opened this issue · comments

Our current way to realise the infix API is to duplicate a lot of the fluent API. However, most of the times, we only add the infix keyword to functions, which could also simply add to the fluent API without making it less usable. So maybe we can avoid this duplication?

I see two options here:

  1. Still create different bundles for the fluent and the infix API, but make them share most of their (API) code.
  2. Don’t even create different bundles, just include the additional types and functions needed for the infix API in the fluent API.

The downside of adding infix to the fluent API is that clients may end up with a codebase that has a mixed use of styles.

this would go against requirement R1 https://github.com/robstoll/atrium-roadmap/wiki/Requirements#r1-separation-of-infix-and-fluent-api

IMO either we have a separate API or we keep only fluent

Yeah, I figured you’d say that. I didn’t remember R1, so I wanted to bring it up, at least.

Do you know whether people use, or plan to use the infix API?

there have been people opening an issue that infix is not available for 0.9.0-alpha so I guess there are people. But I have no idea how many. And unfortunately it is quite hard to get feedback from the consumers

Okay. So since there seem to be people wanting the infix-API and since we arguments to justify the code duplication, I’ll close this.

Let's think about it a bit longer since you brought it up. I was already thinking that we could generate most functions instead of copy pasting it. This way we only have to maintain the fluent API for those parts. What do you think?

Puh… This is a difficult question. I see the advantage, however, it would also mean that the build logic would get more complicated. This always comes with the disadvantage that it’s harder to understand for newcomers how the project works. It also gives the generated code less visibility, with the danger of it breaking in some way that is not observed.

I'll let you think about it 😉

I thought a little more about it, here is my take:

The more I think of it, the more I think that introducing two API styles is a decision with huge consequences. I like atrium so much that I think it should become the go-to assertion library for Kotlin. But that means that maintainability will play a significant role. Because the more one has to do to extend atrium, the less likely it is that it will happen.

Having two API styles makes it harder for both

  • developers who want to maintain and publish their own atrium assertions
  • developers who want to contribute to atrium itself

because it can mean double the work. And as we have seen in robstoll/atrium#371, developing for the infix API can be particularly tricky.

So with that, maybe we should ask ourselves: Is having the infix API worth making it harder to develop for atrium? Open-source projects are usually only successful in the long run if there are a lot of contributions. This is not meant to be a leading question though. I mean it as an honest, difficult question we should think about. Nevertheless, I am biased because I like fluent APIs and don’t see what improvements infix APIs bring.

Coming back to code generators: We could circumvent all of this if we had a code generator that can generate an infix API for at least 90% of all cases. This way, developers would not have to worry about it. However, I have my doubts about whether we can build such a generator that will do that reliably.

IMO it is perfectly fine if:

  • the infix API lacks behind the fluent
  • a contributor only provides the fluent API; I would simply create an issue for the infix API, some can take this one over => in this sense it will show if the infix API is used enough.
  • a contributor only provides a function on the API level

What I see as a problem is for third-party libraries. Say you want to provide an extension for Atrium for your lib A. Do you write it only for the fluent API or also for the infix?

this way, developers would not have to worry about it. However, I have my doubts about whether we can build such a generator that will do that reliably.

Challenge accepted 😆 but let us first think about whether we want to support the infix API or not (I have dropped de_CH for the same reason)

I was mainly thinking about third-party libraries. But I also don’t buy your arguments fully for pull requests, because:

  • if a little group of maintainers has to do the work for the infix API because most contributors don’t, then the value of contributions is lowered and development is slowed down.
  • if the infix API lacks behind the fluent API, people will surely get annoyed by the missing functionality. So they will either abandon the infix API (and then: why are we doing it?) or abandon atrium altogether.

I think my point is: If we are going to do the infix API, then we have to do right and make sure it’s not lacking anything the fluent API can do.


let us first think about whether we want to support the infix API or not

I think there are two relevant questions:

  1. Is there anything objectively better about the infix API when compared to the fluent API? What I mean by that is reasons that come down to more than just personal taste. For example, if there are people that find reading or writing the infix API significantly easier, that would be a good reason. If there no such reasons, that might be an argument to not support it. Because as you have already argued elsewhere choice can be a bad thing, and personal taste has to step back if there are rational arguments.

  2. Is it likely that developers will choose another library over atrium because of the lacking infix API? Even if there are no good reasons to have an infix API, we might still support it because developers just like it so much and we would lose them if we do not support it.

If the answer to both of these questions was “no”, I would suggest dropping the infix API. Because there is an objective argument against it: Maintaining two APIs is considerable additional effort, no matter how we do it. And as we have seen, the infix API has a higher tendency to lead to situations where there are ambiguities or other unexpected properties of the API.

if the infix API lacks behind the fluent API, people will surely get annoyed by the missing functionality.

The infix API already lacks floating point assertions, no one bothered so far or rather, no one opened an issue. I have the feeling missing features is not really such a problem in Atrium (regardless of which API) as most of new features are just enhancements. You can already do everything with the feature set available since 0.6.0 or so. What we add in addition are shortcuts, better error reports etc. Which is great, I don't want to downplay the improvements we make.

I completely agree with your two questions, the only problem is, that I cannot answer the questions. I am not using the infix API myself. Though I considered to switch to it for the following reason:

  • I am much faster writing assertion if I don't have to press . => somehow my brain always makes a stop at each . (luckily not a full stop)

But I have not done it so far because I am not actively working on a new Kotlin project where I could try it out (and I don't want to mix)

It would be best if we had a core contributor which is an advocate for the infix API. So far we only use the fluent API I think, so this voice is kind of missing. I'll try to get a voice from slack.

Please react with 👍 in case you use the infix API and want to keep it

I am bit out of the loop. What has the decision been here? The small number of reactions might suggest that we don’t actually need an infix API. On the other hand, I have seen a lot of work on the infix API. How will we proceed?

I will release the infix API which is Expect based with 0.11.0. This version should cover most cases and people wanting to use the infix API should be able to use it for a lot of cases.
Also, I thought that I am going to publish it to a different project on bintray. This way we can see at least a difference in numbers. I would then decide if I am going to keep it in 1.0.0 and beyond or drop it at that stage.

I see. Thanks! 👍

I finally found better download statistics on bintray:
https://bintray.com/robstoll/tutteli-jars/atrium#statistics

Looking at 24h:

api downloads
cc-infix 249
cc-en_GB 463
fluent-en_GB 369

I guess we have to subtract 249 from cc-en_GB as cc-infix includes it indirectly.

api downloads
cc-infix 249
cc-en_GB 214
fluent-en_GB 369

From those numbers it seems infix is heavily in use.

Wow, that’s impressive. A good argument to keep developing it!

Coming back to this issue, I think we can close it. Creating a new API is a huge one-time effort, however, I feel that maintaining it is not. Since we want to keep APIs as stable as possible, it follows that we modify them seldom.

De-duplication is mainly a means to reduce maintenance effort. Since I expect the APIs to cause very little maintenance effort, I think we can tolerate it. Writing an error-prone generator also requires considerable effort.