New version of itself

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

New version of itself

Martin Drautzburg
Hello all,

I find myself frequentlly writing types like this

data Logger a l = Lgr {
  runLogger :: a -> Log l -> (Log l, Logger a l)
  }

The purpose is to give a logger a chance to carry an internal state. It could e.g. choose to log only every n
invocations. To do this it must keep track of how many times it has been called. I want to leave such things private to
the Logger.


(1) I know that this is not an unusal thing to do, but it has an OO feel to it. Is there a more functional alternative
to it. Should I just not worry?

(2) I can write a combinator which creates a Logger from a list of Loggers. Since each Logger potentially returns a new
version of itself, I must always re-assemble the combined logger from all the returned new versions. I am worried that
this is a costly operation, particularly when most Loggers just return themselves unaltered. I don't have any hard
numbers about the performance penalty though.

These Loggers are used in a discrete-event-simulation and they will get called many times (once for each event), but
only occastionally actually write to the Log.
_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: New version of itself

Alex Belanger

Before I go into crazy suggestions, have you looked at the Writer monad?

On Jun 4, 2016 9:25 AM, "martin" <[hidden email]> wrote:
>
> Hello all,
>
> I find myself frequentlly writing types like this
>
> data Logger a l = Lgr {
>   runLogger :: a -> Log l -> (Log l, Logger a l)
>   }
>
> The purpose is to give a logger a chance to carry an internal state. It could e.g. choose to log only every n
> invocations. To do this it must keep track of how many times it has been called. I want to leave such things private to
> the Logger.
>
>
> (1) I know that this is not an unusal thing to do, but it has an OO feel to it. Is there a more functional alternative
> to it. Should I just not worry?
>
> (2) I can write a combinator which creates a Logger from a list of Loggers. Since each Logger potentially returns a new
> version of itself, I must always re-assemble the combined logger from all the returned new versions. I am worried that
> this is a costly operation, particularly when most Loggers just return themselves unaltered. I don't have any hard
> numbers about the performance penalty though.
>
> These Loggers are used in a discrete-event-simulation and they will get called many times (once for each event), but
> only occastionally actually write to the Log.
> _______________________________________________
> Haskell-Cafe mailing list
> [hidden email]
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: New version of itself

Daniel Díaz Carrete
In reply to this post by Martin Drautzburg
This is a bit of a tangent, but one can use the FoldM type from the foldl package to build composable logger-like things that perform effects.

The semigroupoids package contains the Extend typeclass, which is basically "Comonad without extract". If we make FoldM an instance of Extend, we can "single-step" a FoldM with the function:

     import Data.Functor.Extend (duplicated)
     import qualified Control.Foldl as L

     singleStep :: i -> L.FoldM IO i r -> IO (L.FoldM IO i r)
     singleStep i :: flip L.foldM [i] . duplicated

Which is basically a logging function.

Also, since FoldM is an instance of Applicative, you can combine a list of loggers using sequenceA_.

Another way to define a logger type would be with Cofree from the free package. Something like:

     type Logger = Cofree ((->) String) (IO ()) 

Which would also be an Applicative. One could then define stateful loggers using Cofree's <a href="http://One then could define loggers using unfold. ">unfold.

I think the Cofree version has the advantage that you can be sure the logger's state only depends on the messages it receives (even as it emits IO actions on each step). The FoldM version, meanwhile, could perform some devious IO action to update its state.

On Saturday, June 4, 2016 at 3:24:57 PM UTC+2, martin wrote:
Hello all,

I find myself frequentlly writing types like this

data Logger a l = Lgr {
  runLogger :: a -> Log l -> (Log l, Logger a l)
  }

The purpose is to give a logger a chance to carry an internal state. It could e.g. choose to log only every n
invocations. To do this it must keep track of how many times it has been called. I want to leave such things private to
the Logger.


(1) I know that this is not an unusal thing to do, but it has an OO feel to it. Is there a more functional alternative
to it. Should I just not worry?

(2) I can write a combinator which creates a Logger from a list of Loggers. Since each Logger potentially returns a new
version of itself, I must always re-assemble the combined logger from all the returned new versions. I am worried that
this is a costly operation, particularly when most Loggers just return themselves unaltered. I don't have any hard
numbers about the performance penalty though.

These Loggers are used in a discrete-event-simulation and they will get called many times (once for each event), but
only occastionally actually write to the Log.
_______________________________________________
Haskell-Cafe mailing list
<a href="javascript:" target="_blank" gdf-obfuscated-mailto="AaA3yoFJBAAJ" rel="nofollow" onmousedown="this.href=&#39;javascript:&#39;;return true;" onclick="this.href=&#39;javascript:&#39;;return true;">Haskel...@...
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" target="_blank" rel="nofollow" onmousedown="this.href=&#39;http://www.google.com/url?q\x3dhttp%3A%2F%2Fmail.haskell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fhaskell-cafe\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNH7sFgl7KfuDcDlaGGG3ip3kRaoIA&#39;;return true;" onclick="this.href=&#39;http://www.google.com/url?q\x3dhttp%3A%2F%2Fmail.haskell.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fhaskell-cafe\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNH7sFgl7KfuDcDlaGGG3ip3kRaoIA&#39;;return true;">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: New version of itself

Carl Howells-2
That's basically a Mealy machine.  See
https://hackage.haskell.org/package/machines-0.6/docs/Data-Machine-Mealy.html

newtype Logger a l = Logger (Mealy (a, Log l) (Log l))

Not quite an exact isomorphism since (,) is lifted, but it's close.

You're not actually doing anything weird there. That's a pretty
standard functional representation of a state machine.

On Sat, Jun 4, 2016 at 8:00 AM, Daniel Díaz <[hidden email]> wrote:

> This is a bit of a tangent, but one can use the FoldM type from the foldl
> package to build composable logger-like things that perform effects.
>
> The semigroupoids package contains the Extend typeclass, which is basically
> "Comonad without extract". If we make FoldM an instance of Extend, we can
> "single-step" a FoldM with the function:
>
>      import Data.Functor.Extend (duplicated)
>      import qualified Control.Foldl as L
>
>      singleStep :: i -> L.FoldM IO i r -> IO (L.FoldM IO i r)
>      singleStep i :: flip L.foldM [i] . duplicated
>
> Which is basically a logging function.
>
> Also, since FoldM is an instance of Applicative, you can combine a list of
> loggers using sequenceA_.
>
> Another way to define a logger type would be with Cofree from the free
> package. Something like:
>
>      type Logger = Cofree ((->) String) (IO ())
>
> Which would also be an Applicative. One could then define stateful loggers
> using Cofree's unfold.
>
> I think the Cofree version has the advantage that you can be sure the
> logger's state only depends on the messages it receives (even as it emits IO
> actions on each step). The FoldM version, meanwhile, could perform some
> devious IO action to update its state.
>
> On Saturday, June 4, 2016 at 3:24:57 PM UTC+2, martin wrote:
>>
>> Hello all,
>>
>> I find myself frequentlly writing types like this
>>
>> data Logger a l = Lgr {
>>   runLogger :: a -> Log l -> (Log l, Logger a l)
>>   }
>>
>> The purpose is to give a logger a chance to carry an internal state. It
>> could e.g. choose to log only every n
>> invocations. To do this it must keep track of how many times it has been
>> called. I want to leave such things private to
>> the Logger.
>>
>>
>> (1) I know that this is not an unusal thing to do, but it has an OO feel
>> to it. Is there a more functional alternative
>> to it. Should I just not worry?
>>
>> (2) I can write a combinator which creates a Logger from a list of
>> Loggers. Since each Logger potentially returns a new
>> version of itself, I must always re-assemble the combined logger from all
>> the returned new versions. I am worried that
>> this is a costly operation, particularly when most Loggers just return
>> themselves unaltered. I don't have any hard
>> numbers about the performance penalty though.
>>
>> These Loggers are used in a discrete-event-simulation and they will get
>> called many times (once for each event), but
>> only occastionally actually write to the Log.
>> _______________________________________________
>> Haskell-Cafe mailing list
>> [hidden email]
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>
>
> _______________________________________________
> Haskell-Cafe mailing list
> [hidden email]
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>
_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: New version of itself

Martin Drautzburg
Carl,

thanks for pointing me towards mealy machines.

Maybe you can help me with this one: I am trying to compose two mealy machines into one. Suppose I have

m1 :: Mealy a Bool
m2 :: Mealy a Bool

now I want a combinded mealy machine which outputs True when both m1 and m2 return True and False otherwise. I can write
this using runMealy. But this looks awkward, because it is a bare-bones implementation and doesn't make use of any of
the typeclasses Mealy is a member of. I.e. the machines package is basically not used at all.



Am 06/04/2016 um 07:10 PM schrieb Carl Howells:

> That's basically a Mealy machine.  See
> https://hackage.haskell.org/package/machines-0.6/docs/Data-Machine-Mealy.html
>
> newtype Logger a l = Logger (Mealy (a, Log l) (Log l))
>
> Not quite an exact isomorphism since (,) is lifted, but it's close.
>
> You're not actually doing anything weird there. That's a pretty
> standard functional representation of a state machine.
>

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: New version of itself

Erik Hesselink
Since `Mealy a` is an instance of `Applicative` you can do:

  (&&) <$> m1 <*> m2

It's also a monad, so do notation would also work.

Erik

On 6 June 2016 at 10:00, martin <[hidden email]> wrote:

> Carl,
>
> thanks for pointing me towards mealy machines.
>
> Maybe you can help me with this one: I am trying to compose two mealy machines into one. Suppose I have
>
> m1 :: Mealy a Bool
> m2 :: Mealy a Bool
>
> now I want a combinded mealy machine which outputs True when both m1 and m2 return True and False otherwise. I can write
> this using runMealy. But this looks awkward, because it is a bare-bones implementation and doesn't make use of any of
> the typeclasses Mealy is a member of. I.e. the machines package is basically not used at all.
>
>
>
> Am 06/04/2016 um 07:10 PM schrieb Carl Howells:
>> That's basically a Mealy machine.  See
>> https://hackage.haskell.org/package/machines-0.6/docs/Data-Machine-Mealy.html
>>
>> newtype Logger a l = Logger (Mealy (a, Log l) (Log l))
>>
>> Not quite an exact isomorphism since (,) is lifted, but it's close.
>>
>> You're not actually doing anything weird there. That's a pretty
>> standard functional representation of a state machine.
>>
>
> _______________________________________________
> Haskell-Cafe mailing list
> [hidden email]
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: New version of itself

Chris Wong-2
In reply to this post by Martin Drautzburg
On Mon, Jun 6, 2016 at 8:00 PM, martin <[hidden email]> wrote:

> Carl,
>
> thanks for pointing me towards mealy machines.
>
> Maybe you can help me with this one: I am trying to compose two mealy machines into one. Suppose I have
>
> m1 :: Mealy a Bool
> m2 :: Mealy a Bool
>
> now I want a combinded mealy machine which outputs True when both m1 and m2 return True and False otherwise. I can write
> this using runMealy. But this looks awkward, because it is a bare-bones implementation and doesn't make use of any of
> the typeclasses Mealy is a member of. I.e. the machines package is basically not used at all.

Hi Martin,

I'm not terribly familiar with machines, but since Mealy is an
Applicative you can try

    (&&) <$> m1 <*> m2

Hope this helps.

>
> Am 06/04/2016 um 07:10 PM schrieb Carl Howells:
>> That's basically a Mealy machine.  See
>> https://hackage.haskell.org/package/machines-0.6/docs/Data-Machine-Mealy.html
>>
>> newtype Logger a l = Logger (Mealy (a, Log l) (Log l))
>>
>> Not quite an exact isomorphism since (,) is lifted, but it's close.
>>
>> You're not actually doing anything weird there. That's a pretty
>> standard functional representation of a state machine.
>>
>
> _______________________________________________
> Haskell-Cafe mailing list
> [hidden email]
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe



--
Chris Wong (https://lambda.xyz)

"I had not the vaguest idea what this meant and when I could not
remember the words, my tutor threw the book at my head, which did not
stimulate my intellect in any way." -- Bertrand Russell
_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: New version of itself

Martin Drautzburg
In reply to this post by Erik Hesselink
Applicative does the trick. But using do-notation was my first attempts, but I could not figure it out. Can you see how
to do this (other than using `ap`)?


Am 06/06/2016 um 10:20 AM schrieb Erik Hesselink:

> Since `Mealy a` is an instance of `Applicative` you can do:
>
>   (&&) <$> m1 <*> m2
>
> It's also a monad, so do notation would also work.
>
> Erik
>
> On 6 June 2016 at 10:00, martin <[hidden email]> wrote:
>> Carl,
>>
>> thanks for pointing me towards mealy machines.
>>
>> Maybe you can help me with this one: I am trying to compose two mealy machines into one. Suppose I have
>>
>> m1 :: Mealy a Bool
>> m2 :: Mealy a Bool
>>
>> now I want a combinded mealy machine which outputs True when both m1 and m2 return True and False otherwise. I can write
>> this using runMealy. But this looks awkward, because it is a bare-bones implementation and doesn't make use of any of
>> the typeclasses Mealy is a member of. I.e. the machines package is basically not used at all.

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: New version of itself

Erik Hesselink
do r1 <- m1
   r2 <- m2
   return (r1 && r2)

Erik

On 6 June 2016 at 11:11, martin <[hidden email]> wrote:

> Applicative does the trick. But using do-notation was my first attempts, but I could not figure it out. Can you see how
> to do this (other than using `ap`)?
>
>
> Am 06/06/2016 um 10:20 AM schrieb Erik Hesselink:
>> Since `Mealy a` is an instance of `Applicative` you can do:
>>
>>   (&&) <$> m1 <*> m2
>>
>> It's also a monad, so do notation would also work.
>>
>> Erik
>>
>> On 6 June 2016 at 10:00, martin <[hidden email]> wrote:
>>> Carl,
>>>
>>> thanks for pointing me towards mealy machines.
>>>
>>> Maybe you can help me with this one: I am trying to compose two mealy machines into one. Suppose I have
>>>
>>> m1 :: Mealy a Bool
>>> m2 :: Mealy a Bool
>>>
>>> now I want a combinded mealy machine which outputs True when both m1 and m2 return True and False otherwise. I can write
>>> this using runMealy. But this looks awkward, because it is a bare-bones implementation and doesn't make use of any of
>>> the typeclasses Mealy is a member of. I.e. the machines package is basically not used at all.
>
_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: New version of itself

Martin Drautzburg
Am 06/06/2016 um 12:42 PM schrieb Erik Hesselink:
> do r1 <- m1
>    r2 <- m2
>    return (r1 && r2)
>
> Erik
>
> On 6 June 2016 at 11:11, martin <[hidden email]> wrote:
>> Applicative does the trick. But using do-notation was my first attempts, but I could not figure it out. Can you see how
>> to do this (other than using `ap`)?

I thought this was what I tried, but I must have made a silly mistake somewhere. After your reassurance it works. Thanks.

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe