gluon-lang / gluon

A static, type inferred and embeddable language written in Rust.

Home Page:https://gluon-lang.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Function to convert List to Array or to sort Array by key?

Boscop opened this issue · comments

I found the function to convert a List to an Array (of), but it seems there is no function to do the reverse?
It would be useful to have it in the std library, e.g.:

let list_to_arr l = 
    match l with
        | Nil -> [] 
        | Cons head tail -> append [head] (list_to_arr tail)

Or (with tail call & accumulator):

let list_to_array l = 
    let to_array acc l =
        match l with
            | Nil -> acc
            | Cons head tail -> to_array (append acc [head]) tail
    to_array [] l

Btw, does gluon do any tail call optimization? :)

I'm asking because I'm working with a lot of long arrays and I need to often sort them.
The only sort function I found in std is for lists, but I need sort_by_key for arrays.

I tried doing

fn sort_by_key<T, K: std::cmp::Ord>(mut arr: Vec<T>, f: &dyn Fn(&T) -> K) -> Vec<T> {
	arr.sort_by_key(f);
	arr
}
// ...
ExternModule::new(vm, record! {
    sort_by_key => primitive!(2, sort_by_key),
    // ...
})

but I got all kinds of errors because of the generics and because the Fn can't cross the FFI boundary. Is there any way to make it work? :)

Then I tried to write sort_by_key for arrays in the repl, like this (based on sort for List):

let { append, Array } = import! std.array
let { List, of, ? } = import! std.list

let list_to_array l = 
    let to_array acc l =
        match l with
            | Nil -> acc
            | Cons head tail -> to_array (append acc [head]) tail
    to_array [] l

let sort_by_key arr key : forall a k. [Ord k] -> Array a -> (a -> k) -> Array a =
    let prelude @ { Ordering, ? } = import! std.prelude
    let { Semigroup, Monoid, Eq, Show } = prelude
    let { Functor, Applicative, Alternative, Monad } = prelude
    let { Foldable } = import! std.foldable
    let { Traversable } = import! std.traversable
    let { Bool } = import! std.bool
    let array @ { ? } = import! std.array
    let { (<>) } = import! std.semigroup
    let { compare } = import! std.cmp
    let { map } = import! std.functor
    let { (<*>), wrap } = import! std.applicative
    let { (<|>) } = import! std.alternative
    let { List, of, ? } = import! std.list
    rec let scan compare xs less equal greater : (a -> Ordering)
            -> List a
            -> List a
            -> List a
            -> List a
            -> (List a, List a, List a)
        =
        match xs with
        | Nil -> (less, equal, greater)
        | Cons y ys ->
            match compare y with
            | LT -> scan compare ys (Cons y less) equal greater
            | EQ -> scan compare ys less (Cons y equal) greater
            | GT -> scan compare ys less equal (Cons y greater)
    in
    rec
    let sort xs : /*[Ord a] ->*/ List a -> List a =
        match xs with
        | Nil -> Nil
        | Cons pivot ys ->
            let (less, equal, greater) = scan (\a -> compare (key a) (key pivot)) ys Nil (Cons pivot Nil) Nil
            sort less <> equal <> sort greater
    list_to_array (sort (of arr))

sort_by_key [{ t = 1., k = "a" }, {t = 0., k = "b" }] (\e -> e.t)

It works but it's quite slow.
Ideally I'd want to use the FFI because I'm working with large arrays of thousands of items and the sorting should be fast if possible :)