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

Array.fromOneItem #109

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

jmagaram
Copy link
Contributor

@jmagaram jmagaram commented Mar 17, 2023

Creates an array from a single value. Includes documentation and tests. Zero cost binding to Array.of. F# has this by the name Array.singleton. Useful in pipeline mode to convert a single value into an array. Other possible names fromOne, fromOneValue, fromSingleItem, fromSingleValue, fromSingleton.

@jmagaram jmagaram changed the title Array.fromSingleton Array.fromOneItem Mar 20, 2023
@zth
Copy link
Collaborator

zth commented Mar 21, 2023

What about calling it Array.fromValue?

A bit on the fence on the general usefulness of this. Do you have some concrete real world examples to share?

@jmagaram
Copy link
Contributor Author

Good question.

The designers of F# included an empty and singleton method for each collection type - list, array, set, sequence.

This is useful in pipeline mode where you end up with a single value and want to turn it into an array for further processing. I think It flows more nicely using Array.fromValue rather than writing an anonymous function. See here...

let x =
  "abc"
  ->String.trim
  ->String.slice(~start=3, ~end=9)
  ->String.concat("!")
  ->(i => [i])
  ->Array.concat(["abc", "def"])

let x =
  "abc"
  ->String.trim
  ->String.slice(~start=3, ~end=9)
  ->String.concat("!")
  ->Array.fromOneValue
  ->Array.concat(["abc", "def"])

Years ago I programmed the board game Hive in F# and found this snippet. It is a function that returns a list of legal moves. In most cases the function naturally calculates a list of possibilities. But there are a couple cases where I know the result will just be a single possible move and so these become singletons.

let placeTargets player board =
        match board |> Board.pieceCount with
        | 0 -> Hex.origin |> Seq.singleton // First tile must go in "center", not touching anything
        | 1 -> Hex.origin |> Hex.north |> Seq.singleton  
            // Second tile can (must) touch opposite color. Although it is legal to move anywhere that 
            // surrounds the initial tile, here I artificially limit the choices to the north of the origin 
            // so that when the computer considers possible moves it can think ahead farther.
        | _ ->
            board
            |> Board.tops
            |> Seq.filter (fun (hex, tile) -> tile.Color=player)
            |> Seq.collect (fun (hex, _) -> hex |> Hex.neighbors)
            ....more stuff here

I found this in fp-ts - https://gcanti.github.io/fp-ts/modules/Array.ts.html#of

I searched Github. This is a popular project. They use the singleton method a few times.

Ionide

https://github.com/lefthandedgoat/canopy/search?q=singleton
https://github.com/project-everest/vale/search?q=singleton

Honestly I couldn't find many F# projects using this. It definitely is not essential - you can always work around it by wrapping that single value in [x] or using an anonymous function in pipeline mode. It is just a nice convenience and sometimes feels more natural. It is similar to the array creation function we have where you specify a value and a repeat count, but the repeat count is 1. I can't argue strongly for this. I think if we're going to have an assortment of useful array creation functions then this fits in nicely and I doubt anyone is going to get confused by what it does.

@glennsl
Copy link
Contributor

glennsl commented Mar 21, 2023

Why not just call it Array.of, since that's what it binds to?

In Array.fromValue it doesn't seem like Value carries any information at all, since anything is a value. fromItem or fromElement would better suggest that the value becomes an item/element in the array.

@jmagaram
Copy link
Contributor Author

I think it depends on whether we want to have a standard of using from... for the creation functions. I don't have a strong opinion on it, but I kind of like it. If I want to make an array, I can see all the from... functions together in the Intellisense and think "Wow! ReScript Core is awesome giving me all these ways of creating an array" - especially compared to Javascript - and it is easy to find the one I need rather than if the creation functions are scattered alphabetically. from is a pretty short prefix too. I've advocated elsewhere for choices like these.

  let fromRepeat: (~length: int, 'a) => array<'a>
  let fromIndexMap: (~length: int, int => 'a) => array<'a>
  let fromOneItem: 'a => array<'a>
  let fromOption: option<'a> => array<'a>
  let fromFormula: ('state => option<('a, 'state)>, 'state) => array<'a>
  let fromArrayLike: Js.Array2.array_like<'a> => array<'a>
  let fromArrayLikeWithMap: (Js.Array2.array_like<'a>, 'a => 'b) => array<'b>

@glennsl
Copy link
Contributor

glennsl commented Mar 21, 2023

Personally I agree. But since the primary guiding principle is familiarity for JS developers I would expect direct bindings to JS functions to be named the same as their counterpart.

@jmagaram
Copy link
Contributor Author

You can always have both an of and fromOneItem if there are certain functions JS developers are really going to expect and get frustrated they can't find. Of course we don't want to go crazy with this duplication and use it for everything - good judgment needed. We can make the JS developers happy, and satisfy the developers who have left the dark side and want to experience the benefits of ReScript and a well-designed standard library.

@jmagaram
Copy link
Contributor Author

of is reserved so you'd have to do of_ which is ugly and implies it works like JavaScript with any number of arguments. If you want this, of1 might be the best for familiarity for JS developers. I think this is mostly useful in pipeline mode to turn an item into a singleton array without having to write an anonymous function. If it is more than 1 item you might as well just type it out [val1, val2, val3, ...].

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

Successfully merging this pull request may close these issues.

3 participants