hawk icon indicating copy to clipboard operation
hawk copied to clipboard

magic customization

Open gelisam opened this issue 11 years ago • 7 comments

In #66, you mention that you would like the default no-flags behavior to be customizable. Here is how this customization could work while still allowing the default behaviour to depend on the type of the user expression.

In addition to the prelude, there would be a file ~/.hawk/auto.hs containing function definitions. The type and order of those definitions would determine the default behaviour of Hawk as follows.

First, the type of each of those definition needs to be of the form

foo :: a -> ByteString -> ByteString

for example

map_words :: (Read a, Show b) => (a -> b) -> ByteString -> ByteString
map_words f = B.pack
            . P.unlines
            .  L.intercalate " "
            . map (map (P.show . f . P.read))
            . map P.words
            . P.lines
            . B.unpack

Given a user expression e, Hawk would then try to typecheck map_words e, then the next definition, and so on until a match is found. Then we would of course evaluate this application and pass it the input.

Of course, we would have to tweak this idea in order to deal with -d and Rows, but assuming we can figure those out, what do you think of this strategy?

gelisam avatar Jan 07 '14 22:01 gelisam

Sorry for the delay but I had a lot of work to do in these days.

I have mixing feelings about this. First of all, I would like the configuration to be as simple as possible and I think the user should write something like a DSL more than haskell itself. Secondly I would like to reason about a more structured configuration: considering a function to have priority just because it comes before the next one is something that I don't like too much. That said, your solution has the major feature that the user doesn't have to set the flags everytime. He just writes the expression and Hawk infers in a very easy way what is the mode intended by the user. A lot of users expect this and magic is a very important point of hawk in my opinion. That's why I think we should add your magic asap.

My proposal is to use a static configuration but let the user select this kind of magic. Let me explain what I mean by static configuration and then I'll explain how the user could enable your magic and the user of ~/.hawk/auto.hs file as you said.

My proposal is to have a configuration file called ~/.hawk/hawk.hs that contains the configuration in a xmonad style (yes, that again). This kind of configuration is easy to use, is compiled when it changed and leads to a super-fast program where the configuration is hard-coded inside the executable (created from that file). Something like:

main :: IO ()
main = hawk $ defaultConfiguration {
    defaultMode = MapMode -- override the default mode
  , defaultLineSeparator = Nothing -- override the default line separator
  -- all the other configurations are inherited from defaultConfiguration
}

The problem of this kind of configuration is that you can set the mode but you still need command line options to change from one mode to another. Some users could not like this and prefer a more implicit system like your. So we could propose the magic mode. From the configuration perspective this means that the user could do:

main = hawk $ defaultConfiguration {
    defaultMode = MagicMode -- override the default mode
}

This will enable the magic mode by default and it will be possible to fallback to a fixed mode by specifying it from the command line. The magic mode will use the definitions inside ~/.hawk/auto.hs (maybe ~/.hawk/magic.hs? Or even better we could rename MagicMode into AutoMode so that the user can immediately understand what magic is about? I leave this decision to you, it's your toy :-)). This solution is nice because if magic becomes a mode like the others then also people who haven't enabled magic by default could use magic using the command line flag.

Note that my solution could also solve the problem on which magic extension the user could use. Instead of setting the mode to MagicMode, the user could enable a set of magic functionalities in this way:

main = hawk $ defaultConfiguration {
  defaultMode = magicMode [Extension1,Extension2,...]
}

where magicMode returns the MagicMode configured with the given extensions (at this point we should also add a defaultMagicMode that contains the extensions enabled by default for your magic).

This solution is a little chunky but has the major benefit to unite the two worlds: people that like a less magic approach can still configure their environment while people that like magic can enable the magic mode and configure it in the same place.

What do you think?

melrief avatar Jan 08 '14 13:01 melrief

Oh yes, AutoMode is a much better name than MagicMode!

I like the idea of configuring Hawk via Haskell expressions. If you remember we had agreed that AutoMode could have different variations, enabled via pragmas at the top of the prelude, but I was concerned that simple flags like those might not be sufficiently precise to properly configure a system as potentially complicated as AutoMode. Configuring AutoMode, and the rest of Hawk, via a Haskell expression makes a lot of sense!

I think we have already discussed this, but could you remind me why the user needs to call hawk on the default configuration? What is the advantage of that approach compared to only allowing the user to modify the configuration?

config = defaultConfiguration { ... }

gelisam avatar Jan 08 '14 13:01 gelisam

Mine was just a sketch of an hypothetical 1.1 configuration system that could wrap nicely your feature, not the main idea :-). Moreover I think you are right and your idea is better. Let me try to confront the two methods.

My system implies the creation your own executable with the configuration hardcoded and it is (should be) faster at start time because you don't need to load the configuration library. I thought this is desiderable for hawk because it should be faster at start time when the configuration doesn't change. I don't have data about how much faster it is and if it is effectively convenient.

The big drawback of my system is that everytime you change the configuration, the main of hawk must be recompiled and this is slower than recompile just the configuration. Again, I don't know how much slower it is, I think not that much considering that the configuration will be compiled and cached. The second big drawback of my system is that we already have the infrastructure to compile and load a file at runtime. Loading a config file written as you wrote is easy to implement.

I'm not totally sure about the performance differences between the two and I don't like my method more yours. From a time-to-implement point of view yours is easier and that's why probably it's a better idea. What do you think?

p.s. there is also the possibility to use xml/json/yaml/... to configure the system instead of Haskell. I prefer to use Haskell because it is more concise and can be easily checked but for completeness I need to talk about this possibility :-).

melrief avatar Jan 08 '14 15:01 melrief

Another obvious format is the one automatically generated by deriving (Read, Show). In fact we will probably want to use that format to cache the result of the user's configuration-outputting program. On 8 Jan 2014 10:29, "Mario Pastorelli" [email protected] wrote:

Mine was just a sketch of an hypothetical 1.1 configuration system that could wrap nicely your feature, not the main idea :-). Moreover I think you are right and your idea is better. Let me try to confront the two methods.

My system implies the creation your own executable with the configuration hardcoded and it is (should be) faster at start time because you don't need to load the configuration library. I thought this is desiderable for hawk because it should be faster at start time when the configuration doesn't change. I don't have data about how much faster it is and if it is effectively convenient.

The big drawback of my system is that everytime you change the configuration, the main of hawk must be recompiled and this is slower than recompile just the configuration. Again, I don't know how much slower it is, I think not that much considering that the configuration will be compiled and cached. The second big drawback of my system is that we already have the infrastructure to compile and load a file at runtime. Loading a config file written as you wrote is easy to implement.

I'm not totally sure about the performance differences between the two and I don't like my method more yours. From a time-to-implement point of view yours is easier and that's why probably it's a better idea. What do you think?

p.s. there is also the possibility to use xml/json/yaml/... to configure the system instead of Haskell. I prefer to use Haskell because it is more concise and can be easily checked but for completeness I need to talk about this possibility :-).

— Reply to this email directly or view it on GitHubhttps://github.com/gelisam/hawk/issues/70#issuecomment-31844089 .

gelisam avatar Jan 08 '14 16:01 gelisam

We still have a lot to do on our plates. Moving beyond 1.1.

gelisam avatar Feb 17 '14 13:02 gelisam

In case we still want an XMonad-style configuration finle, here is a library which facilitates this format: http://hackage.haskell.org/package/dyre

gelisam avatar Aug 17 '14 13:08 gelisam

Yes, that is a good idea but we decided to load at runtime the configuration instead of compiling it, right? Eventually, in future I will probably create a branch to benchmark the speed of the current strategy vs the one using dyre or something like that.

melrief avatar Aug 17 '14 20:08 melrief