<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>Hi,</p>
    <p>thank you for your feedback Daniel an Toz. I now used (most of)
      your suggestions to improve the code.</p>
    <p>You can find the result as an attachment.</p>
    <p>Can I improve this even further? Also, I have put a question in
      the code (as a comment). Could anyone answer</p>
    <p>that one also?</p>
    <p>I would appreciate any answers, and thanks again for your effort.</p>
    <p>chrysaetos99</p>
    <p><br>
    </p>
    <div class="moz-cite-prefix">On 21.05.20 02:04, 鲍凯文 wrote:<br>
    </div>
    <blockquote type="cite"
cite="mid:CAMjcG+E_0PNZQzukikvOD8dVGWLU4Ov3SEXme-PFPEJ1s5RUQw@mail.gmail.com">
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <div dir="ltr">
        <div>Hi,</div>
        <div><br>
        </div>
        <div>For what it's worth, Daniel's suggestion of:</div>
        <div><br>
        </div>
        <div>```haskell</div>
        <div>isAlphabetic char = elem char ['A'..'Z']</div>
        <div>```</div>
        <div><br>
        </div>
        <div>does read nicer but would have to traverse `['A'..'Z']`
          every time. I think what you have is fine, and although
          relational and boolean operators are also in imperative
          languages, they behave as pure functions even in imperative
          languages if subexpressions don't cause side effects. I don't
          think it's unidiomatic, and especially in this case, "between
          A and Z" means the same thing as "is one of the letters A, B,
          C...Z", so the intent of the function is clear as written.</div>
        <div><br>
        </div>
        <div>Best of all would probably be using `isAlpha` from
          `Data.Char` (in `base`).</div>
        <div><br>
        </div>
        <div>Best,</div>
        <div><br>
        </div>
        <div>toz</div>
        <br>
        <div class="gmail_quote">
          <div dir="ltr" class="gmail_attr">On Wed, May 20, 2020 at 2:41
            PM <<a href="mailto:beginners-request@haskell.org"
              moz-do-not-send="true">beginners-request@haskell.org</a>>
            wrote:<br>
          </div>
          <blockquote class="gmail_quote" style="margin:0px 0px 0px
            0.8ex;border-left:1px solid
            rgb(204,204,204);padding-left:1ex">Send Beginners mailing
            list submissions to<br>
                    <a href="mailto:beginners@haskell.org"
              target="_blank" moz-do-not-send="true">beginners@haskell.org</a><br>
            <br>
            To subscribe or unsubscribe via the World Wide Web, visit<br>
                    <a
              href="http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners"
              rel="noreferrer" target="_blank" moz-do-not-send="true">http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners</a><br>
            or, via email, send a message with subject or body 'help' to<br>
                    <a href="mailto:beginners-request@haskell.org"
              target="_blank" moz-do-not-send="true">beginners-request@haskell.org</a><br>
            <br>
            You can reach the person managing the list at<br>
                    <a href="mailto:beginners-owner@haskell.org"
              target="_blank" moz-do-not-send="true">beginners-owner@haskell.org</a><br>
            <br>
            When replying, please edit your Subject line so it is more
            specific<br>
            than "Re: Contents of Beginners digest..."<br>
            <br>
            <br>
            Today's Topics:<br>
            <br>
               1.  Code Review of Caesar-Cipher (chrysaetos99)<br>
               2. Re:  Code Review of Caesar-Cipher (Daniel van de
            Ghinste)<br>
            <br>
            <br>
----------------------------------------------------------------------<br>
            <br>
            Message: 1<br>
            Date: Wed, 20 May 2020 20:26:37 +0200<br>
            From: chrysaetos99 <<a
              href="mailto:chrysaetos99@posteo.de" target="_blank"
              moz-do-not-send="true">chrysaetos99@posteo.de</a>><br>
            To: <a href="mailto:beginners@haskell.org" target="_blank"
              moz-do-not-send="true">beginners@haskell.org</a><br>
            Subject: [Haskell-beginners] Code Review of Caesar-Cipher<br>
            Message-ID: <<a
              href="mailto:cd08aa1e-1c9b-a609-15df-5bf37508cb96@posteo.de"
              target="_blank" moz-do-not-send="true">cd08aa1e-1c9b-a609-15df-5bf37508cb96@posteo.de</a>><br>
            Content-Type: text/plain; charset="utf-8"; Format="flowed"<br>
            <br>
            Background<br>
            ----------<br>
            I am a total beginner in Haskell, so after reading the
            "Starting <br>
            out"-chapter of "Learn you a Haskell", I wanted to create my
            first <br>
            program that actually does something.<br>
            <br>
            I decided to do the famous Caesar-Cipher.<br>
            <br>
            <br>
            Code<br>
            ----<br>
            See attachment.<br>
            <br>
            <br>
            Question(s)<br>
            <br>
            -----------<br>
            <br>
            - How can this code be improved in general?<br>
            - Do I follow the style guide of Haskell (indentation,
            etc.)?<br>
            - I have a background in imperative languages. Did I do
            something that <br>
            is untypical for functional programming languages?<br>
            <br>
            I would appreciate any suggestions.<br>
            <br>
            ---<br>
            <br>
            Please note: I also asked this question on <br>
            <a
href="https://codereview.stackexchange.com/questions/242529/caesar-cipher-implementation"
              rel="noreferrer" target="_blank" moz-do-not-send="true">https://codereview.stackexchange.com/questions/242529/caesar-cipher-implementation</a>,
            <br>
            but didn't receive an answer that really answered all my
            questions.<br>
            <br>
            <br>
            Kind regards<br>
            <br>
            chrysaetos99<br>
            <br>
            <br>
            -------------- next part --------------<br>
            A non-text attachment was scrubbed...<br>
            Name: caesar.hs<br>
            Type: text/x-haskell<br>
            Size: 1294 bytes<br>
            Desc: not available<br>
            URL: <<a
href="http://mail.haskell.org/pipermail/beginners/attachments/20200520/71a523ea/attachment-0001.hs"
              rel="noreferrer" target="_blank" moz-do-not-send="true">http://mail.haskell.org/pipermail/beginners/attachments/20200520/71a523ea/attachment-0001.hs</a>><br>
            <br>
            ------------------------------<br>
            <br>
            Message: 2<br>
            Date: Wed, 20 May 2020 23:37:18 +0200<br>
            From: Daniel van de Ghinste <<a
              href="mailto:danielvandeghinste@gmail.com" target="_blank"
              moz-do-not-send="true">danielvandeghinste@gmail.com</a>><br>
            To: The Haskell-Beginners Mailing List - Discussion of
            primarily<br>
                    beginner-level topics related to Haskell <<a
              href="mailto:beginners@haskell.org" target="_blank"
              moz-do-not-send="true">beginners@haskell.org</a>><br>
            Subject: Re: [Haskell-beginners] Code Review of
            Caesar-Cipher<br>
            Message-ID: <<a
              href="mailto:E33F7055-8525-4E09-97E2-3947848F4012@gmail.com"
              target="_blank" moz-do-not-send="true">E33F7055-8525-4E09-97E2-3947848F4012@gmail.com</a>><br>
            Content-Type: text/plain; charset="utf-8"<br>
            <br>
            I’m no guru, so somebody please feel free to correct my
            advice here. I’m trying to get a bit more active in reading
            these mailing lists and giving advice as I believe it’ll
            help me become better in my own understnading. <br>
            <br>
            First thing, well done! It works! Nice job for the beginning
            of your haskell journey :) Ill be focusing on your first and
            third question. Your indentations seem fine here so far.<br>
            <br>
            There are many different approaches you could take as you
            learn more, but here are some things I noticed:<br>
            <br>
            ******************** 1)<br>
            isAlphabetic :: Char -> Bool<br>
            isAlphabetic char = char >= 'A' && char <= ‘Z'<br>
            <br>
            I feel this would be more readable as a literal, though you
            function works. The <= && => feel very much
            like imperative style to me<br>
            <br>
            isAlphabetic :: Char -> Bool<br>
            isAlphabetic char = elem char ['A'..'Z']<br>
            <br>
            That feel nicer to me. ‘Elem' works like ‘in’ in some other
            languages and has the type: <br>
            elem :: (Eq a, Foldable t) => a -> t a -> Bool<br>
            <br>
            Lists are a ‘foldable t’ as would be a binary tree (or some
            other type of tree). Just think of ‘foldable’ as something
            you can iterate over/through. It as something you can
            iterate over.<br>
            <br>
            The ‘..’ Syntax in the expression [‘A’..’Z’] works on any
            type which is a member of the ‘Enum’ type class, which is to
            say it has some type of successor/predecessor order. A comes
            after b, c comes after b and so on. <br>
            So typing [‘A’..’Z’] just creates a literal  list of the
            characters from ‘A’ to ‘Z’.<br>
            <br>
            Come to think of it, the above would be even nicer if you
            wrote it in ‘point free style’ (notice the lack of a ‘char’
            in my expected isAlphabetic function parameters, despite it
            being in the type signature) which would require you to
            ‘flip’ the elem function so the Char becomes the second
            argument expected rather than the first<br>
            <br>
            alphabetLength :: Int<br>
            alphabetLength = length alphabet<br>
            <br>
            alphabet :: [Char]<br>
            alphabet = ['A'..'Z']<br>
            <br>
            isAlphabetic :: Char -> Bool<br>
            isAlphabetic = flip elem alphabet<br>
            <br>
            ****************************** 2)<br>
            Try to avoid so much logic in your encrypt  and decrypt
            functions (nested ‘if’ ’then’ ‘else’ makes it harder to
            follow). Also, I don’t know why you are having your
            functions here return an Int and letting your ‘encrypt’ and
            ‘decrypt’ functions do the work of changing them back into a
            Char. It seems to me if you give a Char to your encryptChar
            function, you should get back an encrypted Char. Make these
            functions below finish their work. I assume you were
            tripping yourself up with what to return if isAlphabetic is
            false, but just return the char value unchanged. Then the
            type signature of these 2 functions below could be Char
            -> Int -> Char instead<br>
            <br>
            <br>
            encryptChar :: Char -> Int -> Int<br>
            encryptChar char shift = if isAlphabetic char               
                           -- Only encrypt A...Z<br>
                                        then (if ord char + shift >
            ord 'Z'             -- "Z" with shift 3 becomes "C" and not
            "]" <br>
                                                then ord char + shift -
            alphabetLength<br>
                                              else ord char + shift)<br>
                                     else ord char<br>
            <br>
            decryptChar :: Char -> Int -> Int<br>
            decryptChar char shift = if isAlphabetic char<br>
                                        then (if ord char - shift <
            ord 'A'<br>
                                                then ord char - shift +
            alphabetLength<br>
                                              else ord char - shift)<br>
                                     else ord char<br>
            <br>
            Also, notice how similar the 2 functions are. These could be
            combined. The only difference between them is the ‘+’ and
            ‘-‘ operations switching. Maybe something like the following<br>
            shiftChar :: Int -> Char -> Char<br>
            shiftChar n c<br>
                | isAlphabetic c = chr ((((ord c + n) - base) `mod`
            alphabetLength) + base)<br>
                | otherwise      = c<br>
                   where base = 65<br>
            This version works for negative or positive values of n. So
            it can be used to encrypt and decrypt. My brackets are a bit
            ugly, but I’m getting lazy (it’s late here). <br>
            The use of `mod` here (the infix version of the modulo
            operator, % in some other languages like python) means we
            can accept arbitrarily large values of n to our function,
            and it just wraps around. <br>
            We can also accept negative values.<br>
            The pipe syntax ‘|’ is just a neater way of doing if else
            statements (see guards for more info)<br>
            The 65 here is because ‘ord' is defined for all possible
            Char values. You’re defining your valid range for this
            cipher to capital alphabetic characters, which starts at 65:<br>
            <br>
            *Main> fmap ord ['A'..'Z']<br>
[65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90]
            — the corresponding number for each capital letter in order.<br>
            <br>
            If you haven’t come across fmap yet, it’s the same as map
            for lists, but you can use it on structures other than lists
            - see Functors later in your Haskell education :) <br>
            basically it applies the function to each of the value in
            the list here<br>
            <br>
            ************************************ 3)<br>
            Lastly, your encrypt and decrypt functions<br>
            encrypt :: String -> Int -> String<br>
            encrypt string shift = [chr (encryptChar (toUpper x) shift)
            | x <- string]  -- "Loop" through string to encrypt char
            by char<br>
            <br>
            decrypt :: String -> Int -> String<br>
            decrypt string shift = [chr (decryptChar (toUpper x) shift)
            | x <- string]<br>
            <br>
            I like your use of list comprehensions here :) though as
            mentioned before I don’t think you should be using the chr
            function here, rather your encryptChar and decryptChar
            functions should be doing that to the values they return.<br>
            <br>
            Because I changed other pieces in the code, I’ll show you
            another way of doing this part now.<br>
            <br>
            encrypt' :: Int -> String -> String<br>
            encrypt' x = fmap (shiftChar x)<br>
            <br>
            decrypt' :: Int -> String -> String<br>
            decrypt' x = encrypt' (negate x)<br>
            <br>
            Here I’m using ‘point free’ style again only referencing the
            first of the two parameters expected by encrypt’. Because we
            changed our other function in answer ******2 on this mail,
            encrypt’ and decrypt’ are just the inverse of each other. So
            decrypt' is defined in terms of encrypt’ but just negates
            the number passed to encrypt’. As you might expect, negate
            just makes positive numbers negative, and negative ones
            positive. <br>
            <br>
            In encrypt’ I also use fmap again, because in Haskell a
            String is just a type alias for a list of Characters ( type
            String = [Char]), so we are just mapping the function
            shiftChar, with the number already baked in,  to each
            character in the list of character (string) which will be
            passed to this function.<br>
            <br>
            Please find attached my edits to your source file if you
            wanna see it in full and play around with it.<br>
            <br>
            Best regards,<br>
            Lord_Luvat<br>
            <br>
            P.S. Let me know if I’m a bad teacher, or pitching this
            response at the wrong level :) I’m trying to learn here too.<br>
            <br>
            <br>
            <br>
            > On 20 May 2020, at 20:26, chrysaetos99 <<a
              href="mailto:chrysaetos99@posteo.de" target="_blank"
              moz-do-not-send="true">chrysaetos99@posteo.de</a>>
            wrote:<br>
            > <br>
            > Background<br>
            > ----------<br>
            > I am a total beginner in Haskell, so after reading the
            "Starting out"-chapter of "Learn you a Haskell", I wanted to
            create my first program that actually does something.<br>
            > <br>
            > I decided to do the famous Caesar-Cipher.<br>
            > <br>
            > <br>
            > Code<br>
            > ----<br>
            > See attachment.<br>
            > <br>
            > <br>
            > Question(s)<br>
            > <br>
            > -----------<br>
            > <br>
            > - How can this code be improved in general?<br>
            > - Do I follow the style guide of Haskell (indentation,
            etc.)?<br>
            > - I have a background in imperative languages. Did I do
            something that is untypical for functional programming
            languages?<br>
            > <br>
            > I would appreciate any suggestions.<br>
            > <br>
            > ---<br>
            > <br>
            > Please note: I also asked this question on <a
href="https://codereview.stackexchange.com/questions/242529/caesar-cipher-implementation"
              rel="noreferrer" target="_blank" moz-do-not-send="true">https://codereview.stackexchange.com/questions/242529/caesar-cipher-implementation</a>,
            but didn't receive an answer that really answered all my
            questions.<br>
            > <br>
            > <br>
            > Kind regards<br>
            > <br>
            > chrysaetos99<br>
            > <br>
            > <br>
            >
            <caesar.hs>_______________________________________________<br>
            > Beginners mailing list<br>
            > <a href="mailto:Beginners@haskell.org" target="_blank"
              moz-do-not-send="true">Beginners@haskell.org</a><br>
            > <a
              href="http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners"
              rel="noreferrer" target="_blank" moz-do-not-send="true">http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners</a><br>
            <br>
            -------------- next part --------------<br>
            An HTML attachment was scrubbed...<br>
            URL: <<a
href="http://mail.haskell.org/pipermail/beginners/attachments/20200520/2bb8f793/attachment.html"
              rel="noreferrer" target="_blank" moz-do-not-send="true">http://mail.haskell.org/pipermail/beginners/attachments/20200520/2bb8f793/attachment.html</a>><br>
            -------------- next part --------------<br>
            A non-text attachment was scrubbed...<br>
            Name: Ceasar.hs<br>
            Type: application/octet-stream<br>
            Size: 650 bytes<br>
            Desc: not available<br>
            URL: <<a
href="http://mail.haskell.org/pipermail/beginners/attachments/20200520/2bb8f793/attachment.obj"
              rel="noreferrer" target="_blank" moz-do-not-send="true">http://mail.haskell.org/pipermail/beginners/attachments/20200520/2bb8f793/attachment.obj</a>><br>
            -------------- next part --------------<br>
            An HTML attachment was scrubbed...<br>
            URL: <<a
href="http://mail.haskell.org/pipermail/beginners/attachments/20200520/2bb8f793/attachment-0001.html"
              rel="noreferrer" target="_blank" moz-do-not-send="true">http://mail.haskell.org/pipermail/beginners/attachments/20200520/2bb8f793/attachment-0001.html</a>><br>
            <br>
            ------------------------------<br>
            <br>
            Subject: Digest Footer<br>
            <br>
            _______________________________________________<br>
            Beginners mailing list<br>
            <a href="mailto:Beginners@haskell.org" target="_blank"
              moz-do-not-send="true">Beginners@haskell.org</a><br>
            <a
              href="http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners"
              rel="noreferrer" target="_blank" moz-do-not-send="true">http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners</a><br>
            <br>
            <br>
            ------------------------------<br>
            <br>
            End of Beginners Digest, Vol 143, Issue 5<br>
            *****************************************<br>
          </blockquote>
        </div>
      </div>
      <br>
      <fieldset class="mimeAttachmentHeader"></fieldset>
      <pre class="moz-quote-pre" wrap="">_______________________________________________
Beginners mailing list
<a class="moz-txt-link-abbreviated" href="mailto:Beginners@haskell.org" moz-do-not-send="true">Beginners@haskell.org</a>
<a class="moz-txt-link-freetext" href="http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners" moz-do-not-send="true">http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners</a>
</pre>
    </blockquote>
  </body>
</html>