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

Improve public API #1

Merged
merged 3 commits into from
Sep 2, 2017
Merged

Improve public API #1

merged 3 commits into from
Sep 2, 2017

Conversation

bgrainger
Copy link
Member

  • Return IReadOnlyList so the caller knows the result isn't lazy.
  • Name the items in the returned tuples.

I didn't change the input parameters from IList<T> to ICollection<T> or IEnumerable<T> because the returned tuples assume the ability to index into the lists, so the caller should already have a non-lazy, indexable collection.

I want to stabilise the API for v1.0 so it can be used by JsonPatch in Faithlife.Json.

/// <returns>A sequence of pairs that indicate the differences between the lists.</returns>
/// <remarks><para>Each pair identifies a range of items in each list. The items before and after each range
/// are the same in both lists; the items in each range are the difference. One of the two ranges can be
/// empty, but the index of the range still indicates where the missing items are.</para>
/// <para>EqualityComparer&lt;T>.Default is used to compare items.</para></remarks>
public static IEnumerable<ValueTuple<IndexRange, IndexRange>> FindDifferences<T>(IList<T> listFirst, IList<T> listSecond)
public static IReadOnlyList<(IndexRange FirstRange, IndexRange SecondRange)> FindDifferences<T>(IList<T> first, IList<T> second)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PascalCase is the preferred convention for tuple members?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured that might start some debate...

I'm not arguing for a global convention for all tuples, but in the case where they're a return value, it's consistent with .NET property naming in general and with .Item1 and .Item2 in particular. It seemed like the natural choice for a return value although it does look odd in the method signature.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No objection here. I started with PascalCase, switched to camelCase, but now I'm coming back around...

/// <returns>A sequence of pairs that indicate the differences between the lists.</returns>
/// <remarks><para>Each pair identifies a range of items in each list. The items before and after each range
/// are the same in both lists; the items in each range are the difference. One of the two ranges can be
/// empty, but the index of the range still indicates where the missing items are.</para>
/// <para>EqualityComparer&lt;T>.Default is used to compare items.</para></remarks>
public static IEnumerable<ValueTuple<IndexRange, IndexRange>> FindDifferences<T>(IList<T> listFirst, IList<T> listSecond)
public static IReadOnlyList<(IndexRange FirstRange, IndexRange SecondRange)> FindDifferences<T>(IList<T> first, IList<T> second)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No objection here. I started with PascalCase, switched to camelCase, but now I'm coming back around...

/// <param name="comparer">The comparer.</param>
/// <returns>A sequence of pairs that indicate the differences between the lists.</returns>
/// <remarks><para>Each pair identifies a range of items in each list. The items before and after each range
/// are the same in both lists; the items in each range are the difference. One of the two ranges can be
/// empty, but the index of the range still indicates where the missing items are.</para>
/// <para>If comparer is null, EqualityComparer&lt;T>.Default is used.</para></remarks>
public static IEnumerable<ValueTuple<IndexRange, IndexRange>> FindDifferences<T>(IList<T> listFirst, IList<T> listSecond,
public static IReadOnlyList<(IndexRange FirstRange, IndexRange SecondRange)> FindDifferences<T>(IList<T> first, IList<T> second,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If IList<T> implemented IReadOnlyList<T>, I'd recommend changing it. Alas...

But maybe we should change it anyway, and introduce an easy way to cast/convert an IList<T> to an IReadOnlyList<T> in Faithlife.Utility. AsReadOnlyList() extension method?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arguably, IReadOnlyList<T> is the better argument type, as the implied contract is that the method won't mutate its arguments. I think it's worth changing for that alone.

The only problem with that is a caller who has an IList<T> and can't easily call the method? A new .AsReadOnlyList extension method sounds good. (Try to cast to IReadOnlyList<T>; failing that, execute .ToList()?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't a shim be cheaper than .ToList()? I guess it's hard to say for sure. An indirection vs. copying the items.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be uncommon, so maybe the shim isn't worth the effort.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out I need a shim for JSON patching in Faithlife.Json: JToken is IList<T> but not IReadOnlyList<T>. Adding this in Faithlife/FaithlifeUtility#5.

@ejball
Copy link
Member

ejball commented Sep 1, 2017

@bgrainger, did you ponder replacing IndexRange with another tuple? Or maybe both ranges could be a single tuple?

Copy link
Member

@ddunkin ddunkin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@bgrainger
Copy link
Member Author

did you ponder replacing IndexRange with another tuple?

Not really. I'd prefer to use a framework type. Span<T>? IListSegment<T> (if that existed).

@ejball
Copy link
Member

ejball commented Sep 1, 2017

IReadOnlyList<((int Start, int Length) FirstRange, (int Start, int Length) SecondRange)>

You know you want to...

* Return IReadOnlyList so the caller knows the result isn't lazy.
* Take IReadOnlyList because the collections aren't mutated.
* Name the items in the returned tuples.
Simplify code. Remove unnecessary tests of ValueTuple (a framework type).
This project only uses simple .NET APIs.
@bgrainger bgrainger merged commit 40e0339 into Faithlife:master Sep 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants