Gabriel Guillermo
Gabriel Guillermo

Reputation: 91

Is there some pipe operator to last argument or a way to define it in F#?

I want a piping operator able to pipe as last parameter. For example, with this definitions:

let download  (uri: String) scoped  =
    use client = new HttpClient()
    task {
        let! stream = client.GetStreamAsync(uri)
        let! r = scoped stream
        return r
    }

let decompressStream stream scoped   =
    use decompressedStream = new GZipStream(stream, CompressionMode.Decompress)
    task {
        let! r = scoped decompressedStream
        return r
    }

let writeToTempFile (stream: Stream) =
    let fileName = Path.GetTempFileName()
    use fileStream = File.OpenWrite(fileName)
    task {
        do! stream.CopyToAsync(fileStream)
        return fileName
    }

I want to be able to write this code (suppose the operator is |&> ):

let downloadAndDecompress =
    download |&> decompressStream |&> writeToTempFile

So I can compose functions, putting right side as last parameter (parameter scoped) of the left side of the operator. The operator should be left associative. So at the end, In my example, I would use downloadAndDecompress passing an url and I would get the temp filename with the decompressed content as result.

I'm trying with this code:

type LastPipedArg = 
    static member inline (|&>)( f: 'a1 -> 'r, lastArg: 'a1) =
        f lastArg

    static member inline (|&>)(f: 'a1 -> 'a2 -> 'r, lastArg: 'a2) =
        fun a1 -> f a1 lastArg

    static member inline (|&>)( f: 'a1 -> 'a2 -> 'a3 -> 'r, lastArg: 'a3) =
        fun a1 a2 -> f a1 a2 lastArg
        
    static member inline (|&>)(f: 'a1 -> 'a2 -> 'a3 -> 'a4-> 'r, lastArg: 'a4) =
        fun a1 a2 a3 -> f a1 a2 a3 lastArg

But, I cant get this to work.

So, is there some implementation of this semantics or something very similar? How can implement the custom binary piping operator?

Upvotes: 2

Views: 222

Answers (1)

Brian Berns
Brian Berns

Reputation: 17038

OK, I think I figured this out. The operator you're describing is basically an infix version of what's usually called flip:

let (|&>) f a b =
    f b a

With that, you can define downloadAndDecompress as:

let downloadAndDecompress =
    download |&> (decompressStream |&> writeToTempFile)

The parentheses are needed because the operator is left-associative. Or you can eliminate the need for parens by using a right-associative operator instead, like this:

let (^&>) f a b =
    f b a

let downloadAndDecompress =
    download ^&> decompressStream ^&> writeToTempFile

Upvotes: 2

Related Questions