Help with Chapter 3 / Some questions

Hi

I am going through the book and having some issues with the AddressBook section of chapter 3.

I run spago repl and type:

> import Data.AddressBook
> address = { street: "123 Fake St.", city: "Faketown", state: "CA" }

> showAddress address
"123 Fake St., Faketown, CA"

> entry = { firstName: "John", lastName: "Smith", address: address }

> showEntry entry
"Smith, John: 123 Fake St., Faketown, CA"
  1. Now, if I try to create a new book with book = List entry I get an error Unknown data constructor List – I believe this is because I should not build a new AddressBook that way, but please confirm. It is not clear from the book how I use this type/create new variables of this type (unless with the below point 2.?)…

  2. I can construct a new book by doing emptyBook = book and then add entries by doing book1 = insertEntry entry book.
    Because of immutable data, I assume I need to create a new book with a new name everytime?

  3. The book states:

> :type insertEntry entry
AddressBook -> AddressBook

And this should work if I type it in my repl, since we have ‘type aliased’ (type synonym?) List Entry with AddressBook in Main.purs

However I get:

> :type insertEntry entry
List                                
  { address :: { city :: String     
               , state :: String    
               , street :: String   
               }                    
  , firstName :: String             
  , lastName :: String              
  }                                 
-> List                             
     { address :: { city :: String  
                  , state :: String 
                  , street :: String
                  }                 
     , firstName :: String          
     , lastName :: String           
     }                              

I understand these are equivalent but is it intended?

– Edit

This is also giving me a headache

> findEntry "John" "Smith"
Error found:
in module $PSCI
at <internal>:0:0 - 0:0 (line 0, column 0 - line 0, column 0)

  No type class instance was found for
                                                        
    Data.Show.Show (List                                
                      { address :: { city :: String     
                                   , state :: String    
                                   , street :: String   
                                   }                    
                      , firstName :: String             
                      , lastName :: String              
                      }                                 
                    -> Maybe                            
                         { address :: { city :: String  
                                      , state :: String 
                                      , street :: String
                                      }                 
                         , firstName :: String          
                         , lastName :: String           
                         }                              
                   )                                    
                                                        

while applying a function eval
  of type Eval t1 => t1 -> Effect Unit
  to argument it
while checking that expression eval it
  has type Effect t0
in value declaration $main

where t0 is an unknown type
      t1 is an unknown type

Being the definition findEntry firstName lastName = head <<< filter filterEntry shouldn’t it work with the above args?

Keep in mind the definition for List:

data List a 
  = Nil
  | Cons a (List a)

Cons 1 (Cons 2 (Cons 3 Nil)
==
1 : 2 : 3 : Nil
==
-- 3-element list

I’ll let you figure out how you should answer your first question.

Regarding question, two, yes.

let
 emptyBook = book
 bookPlusEntry1 = insert entry1 book
 bookPlusEntry1And2 = insert entry2 bookPlusEntry1
 -- etc.

Regarding question 3, type aliases are just a convenient way for you to refer to their underlying implementation via a shorter type name. However, they get “desugared” back into their underlying implementation. So, yes, those two are the same, but what you expect to see is likely easier to understand in your particular context.

Regarding the headache, you’re asking the REPL to print out a data type. However, that data type is itself a function (e.g. a -> b). So, how the heck should it do that? It can’t.
If you ignore the AddressBook type alias’s definition part, you’ll see that arrow (i.e. ->), which indicates that you are trying to show a function.
This is occurring because you haven’t fully applied all arguments to that function. Remember that functions are curried.

foo :: a -> b -> c
==
foo :: a -> (b -> c)
==
foo :: Function a (Function b c)

Hey @JordanMartinez, yes indeed I eventually figured it out – just PM’d you in regards

Thanks so much for being so helpful

Question: is there somewhere where the book exerices are solved? Woudl be great to compare my solutions with answers – or I can keep pestering you guys here :innocent:

I was wondering the same thing a while back: PureScript by Example Exercise Solutions

My next plan for the book (after updating some remaining chapters) is to add unit tests for all exercises (similar to exercism.io) and then collect links to completed clones.

1 Like

@milesfrain oh great

I am writing my exercises solutions in a repo. Question:

  • What’s the best way to write tests?

I have wrote some tests for chapter 3 but I am not sure this is even idiomatic

Given src/AddressBook.purs:

module Data.AddressBook where

import Prelude

import Control.Plus (empty)
import Data.List (List(..), filter, head, null)
import Data.Maybe (Maybe)
import Data.Newtype

{- Rest of book code -}

-- Ex 2
findEntryByStreet :: String -> AddressBook -> Maybe Entry
-- Equivalent: findEntryByStreet streetName book = head $ filter filterEntry book
findEntryByStreet streetName = filter filterEntry >>> head
    where
    filterEntry :: Entry -> Boolean
    filterEntry e = e.address.street == streetName

-- Ex 3
isInBook :: String -> String -> AddressBook -> Boolean
isInBook firstName lastName book = not null $ filter filterEntry book
     where 
     filterEntry :: Entry -> Boolean
     filterEntry entry = entry.firstName == firstName && entry.lastName == lastName

Then test/Main.purs

module Test.Main where

import Prelude

import Data.AddressBook 
import Data.List
import Data.Maybe (Maybe(..))
import Effect (Effect)
import Effect.Class.Console (log)
import Test.Assert (assert)


-- Test data
address :: Address
address = { street: "123 Fake St.", city: "Faketown", state: "CA" }

entry :: Entry
entry = { firstName: "John", lastName: "Smith", address: address }

addressbook :: AddressBook
addressbook = insertEntry entry emptyBook


-- Main
main :: Effect Unit
main = do
  assert (findEntry "John" "Smith" addressbook == Just { address: { city: "Faketown", state: "CA", street: "123 Fake St." }, firstName: "John", lastName: "Smith" })
  assert (findEntryByStreet "123 Fake St." addressbook == Just { address: { city: "Faketown", state: "CA", street: "123 Fake St." }, firstName: "John", lastName: "Smith" })
  assert (isInBook "John" "Smith" addressbook == true)

Is there any way to contirbute with these? I’ll be happy to share the repo is is useful?

@JordanMartinez related question: for the test assert to work I need to import Data.Maybe (Maybe(..)) , so including the constructor. Importing Data.Maybe throws error. I found the solution from Phil here: https://github.com/paf31/24-days-of-purescript-2016/blob/master/13.markdown

My question is, I notice in Phil’s code he imports Data.Maybe like import Data.Maybe (Maybe, maybe) with the lowercase maybe. Is that because Maybe is a type and maybeis. function (is function the correct term right? :slight_smile: )

Additionally, 24 days of Purescript seems an interesting project to update, thoughts? Is Phil still involved in Purescript?

Phil isn’t really involved with PureScript any more. I think rather than updating the 24 days repo, it would be preferable to make sure that the things it talks about are documented in the appropriate place, and that that documentation is kept up to date. For example, unicode syntax and all of the various deriving mechanisms should be documented in the language reference, which is part of the purescript/documentation repo (I think they are documented there already), the psc-ide stuff like adding imports or going to definitions should be documented alongside the relevant editor plugin, etc.

It would be great to have readers contribute tests and solutions as they work through the exercises.

Here are some examples of the format that I think will work well:

I also added some more detailed testing instructions to https://github.com/purescript-contrib/purescript-book/issues/79

1 Like

Regarding this test implementation:

main :: Effect Unit
main = do
  assert (findEntry "John" "Smith" addressbook == Just { address: { city: "Faketown", state: "CA", street: "123 Fake St." }, firstName: "John", lastName: "Smith" })
  assert (findEntryByStreet "123 Fake St." addressbook == Just { address: { city: "Faketown", state: "CA", street: "123 Fake St." }, firstName: "John", lastName: "Smith" })
  assert (isInBook "John" "Smith" addressbook == true)

couldn’t the Just { ... } be replaced with Just x where x refers to that value? For example

main :: Effect Unit
main = do
  assert (findEntry "John" "Smith" addressbook == Just address)
  assert (findEntryByStreet "123 Fake St." addressbook == Just entry)
  assert (isInBook "John" "Smith" addressbook == true)

Or am I missing the point?

1 Like

Is that because Maybe is a type and maybe is. function

That’s correct!

-- You could import just the type:
import Data.Maybe (Maybe)
-- a single data constructor
import Data.Maybe (Maybe(Nothing))
-- only specific constructors
import Data.Maybe (Maybe(Nothing, Just))
-- or all of them, which is what people do practically all the time
import Data.Maybe (Maybe(..))

See my Module Syntax folder for more examples like that.

1 Like

couldn’t the Just { ... } be replaced with Just x where x refers to that value? For example

Oh yes, that looks tons better!

However is entry in both the first two tests :slight_smile:

Done with ch3 @milesfrain I saw you added the test - great to see how to properly write tests in PS, will update my own repo later on