What are the benefits of replacing a Haskell record with a function

I read this interesting article about sequels, and I discovered this clever trick. Where I naturally used the notation, instead of it, a function of the sum type is used as its first argument.

So for example, instead

data Processor = Processor { processString :: String -> IO ()
                           , processInt :: Int -> IO ()
                           }


processor = Processor (\s -> print $ "Hello "++ s)
                      (\x -> print $ "value" ++ (show x))

We can do this:

data Arg = ArgString String | ArgInt Int
processor :: Arg -> IO ()
processor (ArgString s) = print "Hello" ++ s
processor (ArgInt x) = print "value" ++ (show x)

Besides smart, what are the benefits of this in a simple recording? Is this a common template and does it have a name?

+4
source share
3 answers

Well, this is just a simple isomorphism. In ADT Algebra:

IO() String& times; IO() Int & Kong; IO() String+Int

The obvious advantage of RHS is that it contains only IO()once - DRY FTW.

+6

, Arg Processor . , , , , ; .

  • . , , . .

  • . , Processor, , . OO.

  • ( ) , Arg. Processor, .

, , , . , Arg Processor , , . , , - - " ", (2) (3) .

+4

, Output Input - ( ). , , {0, 1, 2} 3 {0, 1} 2, , 0 0 1, 1 0 1, 2- 0 1. 2 * 2 * 2 2 3.

+, *, Out In1 + In2= Out In1 * Out In2; :

combiner :: (a -> z, b -> z) -> Either a b -> z
combiner (za, zb) e_ab = case e_ab of Left a -> za a; Right b -> zb b

splitter :: (Either a b -> z) -> (a -> z, b -> z)
splitter z_eab = (\a -> z_eab $ Left a, \b -> z_eab $ Right b)

:

type Processor = Either String Int -> IO ()

? :

  • , . combiner a -> b -> z, , a -> (b -> z) b -> z z. a -> b -> z c -> z, (a, b) -> z, - .
  • ; fst split a combined $ Left a. , - yz . combined ( (yz . fst split, yz . snd split)) . Processor, * -> * Functor.
  • , sum-type.
  • Amount types will look more urgent, so they are likely to be more readable. For example, if I give you the template withProcState p () [Read path1, Apply (map toUpper), Write path2], it’s pretty easy to see that this passes the processor with the commands in upper case 1 to path2. The equivalent of the defining processors will look like procWrite p path2 $ procApply p (map toUpper) $ procRead p path1 ()which is still pretty clear, but not as amazing as in the previous case.
+2
source

All Articles