Search code examples
haskellargs

getArgs string to int


I'm trying to write my first IO program in haskell, but I can't seem to run it from the command line I want the command cabal run 5 > result.txt to print a 5 in binary in result.txt. I found some conversion code but when I implement it I get an error:

src/Main.lhs:23:28: error:
    • Couldn't match type ‘Int’ with ‘Char’
      Expected type: String
        Actual type: [Int]
    • In the second argument of ‘writeFile’, namely ‘(toBin (args))’
      In a stmt of a 'do' block: writeFile "file.txt" (toBin (args))
      In the expression:
        do { args <- getArgs;
             writeFile "file.txt" (toBin (args)) }

src/Main.lhs:23:34: error:
    • Couldn't match expected type ‘Int’ with actual type ‘[String]’
    • In the first argument of ‘toBin’, namely ‘(args)’
      In the second argument of ‘writeFile’, namely ‘(toBin (args))’
      In a stmt of a 'do' block: writeFile "file.txt" (toBin (args))

Here is my code:

module Main where

import System.Environment
import Data.List
import Data.Maybe
import qualified Data.Map as M (Map, empty, insert, lookup)
import Data.Char (ord)

toBin:: Int -> [Int]
toBin 0 = [0]
toBin n = reverse (helper n)

helper:: Int -> [Int]
helper 0 = []
helper n = let (q,r) = n `divMod` 2 in r : helper q

main :: IO ()
main = do
    args <- getArgs
    writeFile "file.txt" (toBin(args))

Solution

  • First, your function toBin expects an Int parameter, but args is of type [String] - i.e. a list of strings. So you need to take the first argument (from your description) and convert it to an Int. The cheapest, dirtiest way to do that is with head to take first and then read to convert to Int:

    writeFile "file.txt" (toBin . read . head $ args)
    

    Note however that this code will crash at runtime if (1) the list of arguments is empty (i.e. doesn't have a "first" element) or (2) the first argument is not a number. If you're not ok with crashing, consider using safer alternatives, such as headMay or reads.


    Second, your function toBin returns a list of Ints, but writeFile expects an argument of type String. The cheapest, dirtiest way to convert is via show:

    writeFile "file.txt" (show . toBin . read . head $ args)
    

    However, the implementation of show for lists will yield a string that doesn't look like a binary number. It will look like "[0, 1, 1, 0, 1]". If you're not ok with this representation, you will have to write your own function for converting the list to a string that looks like a binary number. The cheapest, dirtiest way of doing that is by applying show to each element of the list and then gluing the resulting strings together:

    binToStr :: [Int] -> String
    binToStr = concat . map show
    
    ...
    
    writeFile "file.txt" (binToStr . toBin . read . head $ args)
    

    Or it could be simplified to concatMap:

    binToStr = concatMap show