lazy-seq icon indicating copy to clipboard operation
lazy-seq copied to clipboard

How about adding `reduce` `->vec` and `transpose` function?

Open para100 opened this issue 2 years ago • 5 comments

Firstly thank you for maintaining such a useful lib in fennel. Most my problems is about there are lots of construct function for seq, but merely destruct one. And adding functions below would be very helpful, IMO.

reduce : forall a. forall b. (b -> a -> b) -> b -> Seq a -> b
->vec : forall a. Seq a -> List a
->string: forall a. Seq String -> String
->string = reduce (..) "" 
transpose : forall a. Seq (Seq a) -> Seq (Seq a)

para100 avatar May 06 '22 08:05 para100

You can destructure a sequence with Fennel's destructuring, since sequences can be indexed, and also define a __fennelrest metamethod to avoid realizing the tail. Like so:

(let [[a b c & rest] (range)]
  (print a b c rest))
;;  0	1	2	lazy cons: 0x559c54c0f770

I'm not very good at reading Haskellish type descriptions, but I'll try to answer all these:

  • reduce isn't needed as there's already accumulate in Fennel, and sequences support iterators, so you can do:

    (local nums (range 10))
    (accumulate [res 0 _ x (pairs (range 10))]
      (+ res x)) ;=> 45
    
  • ->vec is currently implemented in tests but only for the comparison purposes, as not all Lua's allow comparing objects with different __eq methods. However, such a simple implementation will not work for broader cases, because, unlike tables, sequences can contain nils, which when put into a table result in holes. The library provides a pack function, that takes a sequence and packs it into a table with size indication, much like table.pack.

    (local s (cons :a (cons nil (cons :c (list)))))
    s ;=> @seq(:a nil :c)
    (pack s) ;=> {1 :a 3 :c :n 3}
    (table.pack :a nil :c) ;=> {1 "a" 3 "c" :n 3}
    
  • As for ->string this is handled by fennel.view module:

    (local fennel (require :fennel))
    (fennel.view (list 1 2 3)) ;=> "@seq(1 2 3)"
    

    This is a general mechanism with additional control options, that works for all data structures that implement the __fennelview metamethod, so a separate ->string is not needed. Lua's tostring also works on sequences, but it returns a more Lua-like "lazy cons: 0x682687623" thing.

  • For transpose there's already map and unpack:

    (map list (unpack (list (list 1 2 3) (list 4 5 6)))) ;=> @seq(@seq(1 4) @seq(2 5) @seq(3 6))
    (map list (unpack [[1 2 3] [4 5 6]])) ;=> @seq(@seq(1 4) @seq(2 5) @seq(3 6))
    ;; and back
    (map list (unpack (map list (unpack (list (list 1 2 3) (list 4 5 6))))))
    ;=> @seq(@seq(1 2 3) @seq(4 5 6))
    

    unpack here serves the same purpose as apply in other lisps.

Hope this answers your questions!

andreyorst avatar May 06 '22 16:05 andreyorst

Additionally, I'm going to use this library in cljlib (eventually) to provide all seq related features via functions from this library. Cljlib has a more broader scope and thus includes some of the functions you've mentioned. For this library the main focus is providing lazy sequences and the most essential operations on them. All things you've described aren't specific to sequences and not necessary lazy, so I'd move them to cljlib or left for user to implement. Granted Fennel already provides everything you need! :smiley:

andreyorst avatar May 06 '22 16:05 andreyorst

Thanks for your detailed explanation and patience to understand my wierd expression.

Your answer solve my problem using this lib. And I will try cljlib later.

Retrospectively, the reason why I struggle to find a lib, is that I want to do everything, especially array like data structure processing, in a functional programming style. In fp world, everything should be combinable. icollect and accumulate work, but cannot combine directly like map, filter, reduce, fold. What I want feels like that, I was a chief and asked for cooking chicken, all I need to do is sealing chicken into a magical black box, and flipping and shaking the box in a specific manner, and finally when open the box, chicken dish is done.

Thanks again for solving my problem.

Cheers

------- Original Message ------- On Saturday, May 7th, 2022 at 12:25 AM, Andrey Listopadov @.***> wrote:

Additionally, I'm going to use this library in cljlib (eventually) to provide all seq related features via functions from this library. Cljlib has a more broader scope and thus includes some of the functions you've mentioned. For this library the main focus is providing lazy sequences and the most essential operations on them. All things you've described aren't specific to sequences and not necessary lazy, so I'd move them to cljlib or left for user to implement. Granted Fennel already provides everything you need! 😃

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.Message ID: @.***>

para100 avatar May 07 '22 00:05 para100

yes, accumulate is a bit hard to compose with the rest of the library, as it's not a function you could thread to or compose with comp. Adding reduce can be done, I just need to think of a proper way to do so, like supporting early termination with reduced

andreyorst avatar May 07 '22 06:05 andreyorst

@para100 I've added reduce and reduced in d902303

Now you can do (reduce #(.. $1 $2) [1 2 3]) and other stuff you would normally need accumulate to. I think that all other cases are now well covered and don't require to be added to the library

andreyorst avatar May 12 '22 16:05 andreyorst