Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

Sorry, you do not have permission to ask a question, You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please type your username.

Please type your E-Mail.

Please choose an appropriate title for the post.

Please choose the appropriate section so your post can be easily searched.

Please choose suitable Keywords Ex: post, video.

Browse

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Navigation

  • Home
  • About Us
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • About Us
  • Contact Us
Home/ Questions/Q 8089

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Latest Questions

Author
  • 60k
Author
Asked: November 28, 20242024-11-28T09:53:09+00:00 2024-11-28T09:53:09+00:00

How to use Haskell to build a todo app with Stack

  • 60k

In this post, we will learn how to build a todo app with Haskell and Stack. Then, you will also learn how to use a Haskell packages along with it also.

The code snippet used here is adapted from this blog.

It wasn't easy to install Haskell with my laptop with m1 chip.

If you haven't done it yet, just use the command what Stack offers you to install it and you will be able to use Haskell along with it also.

$curl -sSL https://get.haskellstack.org/ | sh 
Enter fullscreen mode Exit fullscreen mode

Then, type $stack in your console and that will show the commands similar to this.

test                     Shortcut for 'build --test' new                      Create a new project from a template. Run `stack                            templates' to see available templates. Note: you can                            also specify a local file or a remote URL as a                            template. templates                Show how to find templates available for `stack new'.                            `stack new' can accept a template from a remote                            repository (default: github), local file or remote                            URL. Note: this downloads the help file. init                     Create stack project config from cabal or hpack                            package specifications hoogle                   Run hoogle, the Haskell API search engine. Use the                            '-- ARGUMENT(S)' syntax to pass Hoogle arguments,                            e.g. stack hoogle -- --count=20, or stack hoogle --                            server --local. run                      Build and run an executable. Defaults to the first                            available executable if none is provided as the first                            argument. ghci                     Run ghci in the context of package(s) (experimental) repl                     Run ghci in the context of package(s) (experimental) 
Enter fullscreen mode Exit fullscreen mode

You don't need to know all of them to follow this post. Just read what you want to use and for more information refer to its documentation.

Play with some of the commands first if you haven't yet.

If you need more Haskell examples later, you can refer to this repository also or search for more Haskell todo app relevant information.

Table of Contents

  1. Setup Haskell development environment with Stack
  2. Write Todo app with Haskell
  3. Learn how to use Haskell packages
  4. Conclusion

1. Start Haskell development environment with Stack

To write a todo app with Haskell, we will first set up Haskell development environment with Stack.

For that, we will use $stack new <project> command first.

Use $stack new todo or another name you want to use at your console.

This will show somewhat similar to this at your console.

Downloading template "new-template" to create project "todo" in todo/ ...  The following parameters were needed by the template but not provided: author-name You can provide them in /Users/steadylearner/.stack/config.yaml, like this: templates:   params:     author-name: value Or you can pass each one as parameters like this: stack new todo new-template -p "author-name:value"   The following parameters were needed by the template but not provided: author-email, author-name, category, copyright, github-username You can provide them in /Users/steadylearner/.stack/config.yaml, like this: templates:   params:     author-email: value     author-name: value     category: value     copyright: value     github-username: value Or you can pass each one as parameters like this: stack new todo new-template -p "author-email:value" -p "author-name:value" -p "category:value" -p "copyright:value" -p "github-username:value"  Looking for .cabal or package.yaml files to use to init the project.                  Using cabal packages:                                                                 - todo/                                                                                Selecting the best among 19 snapshots...                                               * Matches https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/15.yaml  Selected resolver: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/15.yaml Initialising configuration using resolver: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/17/15.yaml Total number of user packages considered: 1                                           Writing configuration to file: todo/stack.yaml                                        All done.                                                                             
Enter fullscreen mode Exit fullscreen mode

You can see Stack created various files to help you with $ls todo command.

ChangeLog.md    LICENSE     README.md   Setup.hs    app     package.yaml    src     stack.yaml  stack.yaml.lock test        test-exe    test.cabal 
Enter fullscreen mode Exit fullscreen mode

You don't have to care for all of them. We will just handle app/Main.hs, src/Lib.hs, test/Spec.hs and package.yaml file for this post.

We will start with the package.yaml to use Haskell packages in your project. If you haven't edited the file yet, there will be only one dependency included there similar to this.

dependencies: - base >= 4.7 && < 5 
Enter fullscreen mode Exit fullscreen mode

Include dotenv and open-browser package to your project we will use later.

dependencies: - base >= 4.7 && < 5 - dotenv - open-browser 
Enter fullscreen mode Exit fullscreen mode

Then, test some commands to see everything is ok with them.

First, use $stack test and will show you somewhat similar to this.

$stack test Registering library for todo-0.1.0.0.. todo> test (suite: todo-test)  Test suite not yet implemented  todo> Test suite todo-test passed Completed 2 action(s). 
Enter fullscreen mode Exit fullscreen mode

Then, use $stack run to see your Haskell project compiles and show result at your console.

$stack run someFunc 
Enter fullscreen mode Exit fullscreen mode

If you could see them, you are ready to edit your project to build what can be useful to you.

2. Write Todo app with Haskell

In this part, we will separate to app/Main.hs to set up and run the todo app and src/Lib.hs to provide payload logics to it.

We will also include a simple test in test/Spec.hs to see we can test a function inside src/Lib.hs file.

First, update your Main.hs similar to this.

module Main where  import Lib (prompt)  main :: IO () main = do         putStrLn "Commands:"     putStrLn "+ <String> - Add a TODO entry"     putStrLn "- <Int>    - Delete the numbered entry"     putStrLn "s <Int>    - Show the numbered entry"     putStrLn "e <Int>    - Edit the numbered entry"     putStrLn "l          - List todo"     putStrLn "r          - Reverse todo"     putStrLn "c          - Clear todo"     putStrLn "q          - Quit"     prompt [] -- Start with the empty todo list. 
Enter fullscreen mode Exit fullscreen mode

You can see putStrLn part is just to show what commands you can use for this todo app.

The main logic of the app will be handled with prompt part and we will import it from Lib.hs file we will edit similar to this.

module Lib   ( prompt,     editIndex,   ) where  import Data.List  -- import Data.Char (digitToInt)  putTodo :: (Int, String) -> IO () putTodo (n, todo) = putStrLn (show n ++ ": " ++ todo)  prompt :: [String] -> IO () prompt todos = do   putStrLn ""   putStrLn "Test todo with Haskell. You can use +(create), -(delete), s(show), e(dit), l(ist), r(everse), c(lear), q(uit) commands."   command <- getLine   if "e" `isPrefixOf` command     then do       print "What is the new todo for that?"       newTodo <- getLine       editTodo command todos newTodo     else interpret command todos  interpret :: String -> [String] -> IO () interpret ('+' : ' ' : todo) todos = prompt (todo : todos) -- append todo to the empty or previous todo list [] here. interpret ('-' : ' ' : num) todos =   case deleteOne (read num) todos of     Nothing -> do       putStrLn "No TODO entry matches the given number"       prompt todos     Just todos' -> prompt todos' interpret ('s' : ' ' : num) todos =   case showOne (read num) todos of     Nothing -> do       putStrLn "No TODO entry matches the given number"       prompt todos     Just todo -> do       print $ num ++ ". " ++ todo       prompt todos interpret "l" todos = do   let numberOfTodos = length todos   putStrLn ""   print $ show numberOfTodos ++ " in total"    mapM_ putTodo (zip [0 ..] todos)   prompt todos interpret "r" todos = do   let numberOfTodos = length todos   putStrLn ""   print $ show numberOfTodos ++ " in total"    let reversedTodos = reverseTodos todos    mapM_ putTodo (zip [0 ..] reversedTodos)   prompt todos interpret "c" todos = do   print "Clear todo list."    prompt [] interpret "q" todos = return () interpret command todos = do   putStrLn ("Invalid command: `" ++ command ++ "`")   prompt todos  -- Move the functions below to another file.  deleteOne :: Int -> [a] -> Maybe [a] deleteOne 0 (_ : as) = Just as deleteOne n (a : as) = do   as' <- deleteOne (n - 1) as   return (a : as') deleteOne _ [] = Nothing  showOne :: Int -> [a] -> Maybe a showOne n todos =   if (n < 0) || (n > length todos)     then Nothing     else Just (todos !! n)  editIndex :: Int -> a -> [a] -> [a] editIndex i x xs = take i xs ++ [x] ++ drop (i + 1) xs  editTodo :: String -> [String] -> String -> IO () editTodo ('e' : ' ' : num) todos newTodo =   case editOne (read num) todos newTodo of     Nothing -> do       putStrLn "No TODO entry matches the given number"       prompt todos     Just todo -> do       putStrLn ""        print $ "Old todo is " ++ todo       print $ "New todo is " ++ newTodo       -- let index = head (map digitToInt num)       -- let index = read num::Int       -- print index        let newTodos = editIndex (read num :: Int) newTodo todos -- Couldn't match expected type ‘Int’ with actual type ‘[Char]       let numberOfTodos = length newTodos       putStrLn ""       print $ show numberOfTodos ++ " in total"       mapM_ putTodo (zip [0 ..] newTodos)        prompt newTodos  editOne :: Int -> [a] -> String -> Maybe a editOne n todos newTodo =   if (n < 0) || (n > length todos)     then Nothing     else do       Just (todos !! n)  reverseTodos :: [a] -> [a] reverseTodos xs = go xs []   where     go :: [a] -> [a] -> [a]     go [] ys = ys     go (x : xs) ys = go xs (x : ys)  
Enter fullscreen mode Exit fullscreen mode

There are many functions here but it will knowing what is the difference between : and ++ operators will be most important part to find what they do.

You can refer to this for that.

Please, test each function starting from deleteOne at your console with $stack repl command.

Learn You a Haskell for Great Good! can be a great starting point to help you learn Haskell.

Your Haskell todo app will be ready to be used at this point. Test it with $stack run again and it will show somewhat similar to this at your console.

Registering library for todo-0.1.0.0.. Commands: + <String> - Add a TODO entry - <Int>    - Delete the numbered entry s <Int>    - Show the numbered entry e <Int>    - Edit the numbered entry l          - List todo r          - Reverse todo c          - Clear todo q          - Quit  Test todo with Haskell. You can use +(create), -(delete), s(show), e(dit), l(ist), r(everse), c(lear), q(uit) commands. 
Enter fullscreen mode Exit fullscreen mode

Start with a + command to include a todo. For example, you can use + Write a blog post in your console.

Then, use l to see it is saved in your Haskell todo list. It will show this.

"1 in total" 0: Write a blog psot 
Enter fullscreen mode Exit fullscreen mode

You can include more todo list with + or you can also edit it with e 0 similar to this.

e 0  "What is the new todo for that?" Write ten blog post   "Old todo is Write a blog post" "New todo is Write ten blog post"  "1 in total" 0: Write ten blog post 
Enter fullscreen mode Exit fullscreen mode

You can clear your todo list with c or close the app with q etc.

Test all the commands and relate it with each functions at Lib.hs file. This will help you find how each part of Haskell work better.

You could also find the blog posts explaining what they do.

This chapter from learnyouahaskell will be useful also.

For we could see the app is working, we will write a simple test to verify that $stack test command will work for it.

Update your test/Spec.hs similar to this.

import Control.Exception   import Lib (editIndex)  main :: IO () main = do     putStrLn "Test:"     let index = 1     let new_todo = "two"     let todos = ["Write", "a", "blog", "post"]     let new_todos = ["Write", "two", "blog", "post"]      let result = editIndex index new_todo todos == new_todos      -- assert :: Bool -> a -> a     putStrLn $ assert result "editIndex worked."  
Enter fullscreen mode Exit fullscreen mode

See it work with $stack test and will show you this.

todo> test (suite: todo-test)  Test: editIndex worked.  todo> Test suite todo-test passed Completed 2 action(s). 
Enter fullscreen mode Exit fullscreen mode

Everything was ok and you can include more functions to test if you want.

Thus far, we could learn how to make our first app work with Main.hs to start the app, Lib.hs for payload logic of it and Spec.hs to test it.

Say you liked the todo app a lot and want it to save it as a local executable file.

You can do that with $stack install --local-bin-path . and it will save todo-exe file at your current project folder.

You can test it work with ./test-exe and it will show the same result that you could see with $stack run.

If you want later, you can move it to where your local bin files at.

For example, use $which stack to find the path for it.

$which stack /usr/local/bin/stack 
Enter fullscreen mode Exit fullscreen mode

and move your todo-exe in it with this.

$mv todo-exe todo $mv todo /usr/local/bin 
Enter fullscreen mode Exit fullscreen mode

Then, you will be able to use your Haskell todo app with only $todo command.

3. Learn how to use Haskell packages

In the first part, we already included dotenv and open-browser packages. We will learn how to use them here.

It won't be necessary for your todo app, but it will be helpful learn how to use them if you are a full stack developer and want to verify how the frontend will be after updates from your CLI.

First, create .env file in your project. I will use my GitHub but you can use the production website you work for your client or company.

WEBSITE=https://github.com/steadylearner 
Enter fullscreen mode Exit fullscreen mode

Then, update your Main.hs similar to this.

-- https://www.fpcomplete.com/haskell/tutorial/stack-script/ -- #!/usr/local/bin/env stack -- stack --resolver lts-12.21 script  module Main where  import Configuration.Dotenv (defaultConfig, loadFile) import Lib (prompt) import System.Environment (lookupEnv) import Web.Browser (openBrowser)  -- $stack run  -- $stack build  -- $stack install  -- $stack install --local-bin-path <dir>  -- $stack install --local-bin-path .  -- $./text-exe  -- $stack Main.hs  -- $chmod +x Main.hs  -- $./Main.hs  -- Should include .env and open browser.  main :: IO () main = do   loadFile defaultConfig   website <- lookupEnv "WEBSITE"    case website of     Nothing -> error "You should set WEBSITE at .env file."     Just s -> do       result <- openBrowser s       if result         then print ("Could open " ++ s)         else print ("Couldn't open " ++ s)        putStrLn "Commands:"       putStrLn "+ <String> - Add a TODO entry"       putStrLn "- <Int>    - Delete the numbered entry"       putStrLn "s <Int>    - Show the numbered entry"       putStrLn "e <Int>    - Edit the numbered entry"       putStrLn "l          - List todo"       putStrLn "r          - Reverse todo"       putStrLn "c          - Clear todo"       putStrLn "q          - Quit"       prompt [] -- Start with the empty todo list.  -- putStrLn "Commands:" -- putStrLn "+ <String> - Add a TODO entry" -- putStrLn "- <Int>    - Delete the numbered entry" -- putStrLn "s <Int>    - Show the numbered entry" -- putStrLn "e <Int>    - Edit the numbered entry" -- putStrLn "l          - List todo" -- putStrLn "r          - Reverse todo" -- putStrLn "c          - Clear todo" -- putStrLn "q          - Quit" -- prompt [] -- Start with the empty todo list. 
Enter fullscreen mode Exit fullscreen mode

Test it again with $stack run and will show the website you want to manage along with your CLI app at your console.

You can also see that it could read the WEBSITE from your .env file.

If you could make it, you can include more Haskell packages you want to include an update the app with your own code.

4. Conclusion

In this post, we learnt how to use Stack and made a todo app with Haskell code.

You can find the project used for this post here.

It wasn't easy for me to back to write Haskell code again and I wanted to write this post to help me and others to start to use the language.

I am plan to write more blog posts with Haskell. If you want more contents, please follow me here.

If you need to hire a developer, you can contact me.

Thanks.

beginnershaskelltutorialwebdev
  • 0 0 Answers
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

Sidebar

Ask A Question

Stats

  • Questions 4k
  • Answers 0
  • Best Answers 0
  • Users 2k
  • Popular
  • Answers
  • Author

    How to ensure that all the routes on my Symfony ...

    • 0 Answers
  • Author

    Insights into Forms in Flask

    • 0 Answers
  • Author

    Kick Start Your Next Project With Holo Theme

    • 0 Answers

Top Members

Samantha Carter

Samantha Carter

  • 0 Questions
  • 20 Points
Begginer
Ella Lewis

Ella Lewis

  • 0 Questions
  • 20 Points
Begginer
Isaac Anderson

Isaac Anderson

  • 0 Questions
  • 20 Points
Begginer

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore, ask, and connect. Join our vibrant Q&A community today!

About Us

  • About Us
  • Contact Us
  • All Users

Legal Stuff

  • Terms of Use
  • Privacy Policy
  • Cookie Policy

Help

  • Knowledge Base
  • Support

Follow

© 2022 Querify Question. All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.