Reputation: 622
I'm a newbie in Haskell and I'd like some opinions about improving this script. This is a code generator and requires a command line argument to generate the sql script.
./GenCode "people name:string age:integer"
Code:
import Data.List
import System.Environment (getArgs)
create_table :: String -> String
create_table str = "CREATE TABLE " ++ h (words str)
where h (x:xs) = let cab = x
final = xs
in x ++ "( " ++ create_fields xs ++ ")"
create_fields (x:xs) = takeWhile (/=':') x ++ type x ++ sig
where sig | length xs > 0 = "," ++ create_fields xs
| otherwise = " " ++ create_fields xs
create_fields [] = ""
type x | isInfixOf "string" x = " CHARACTER VARYING"
| isInfixOf "integer" x = " INTEGER"
| isInfixOf "date" x = " DATE"
| isInfixOf "serial" x = " SERIAL"
| otherwise = ""
main = mainWith
where mainWith = do
args <- getArgs
case args of
[] -> putStrLn $ "You need one argument"
(x:xs) -> putStrLn $ (create_table x)
Upvotes: 1
Views: 291
Reputation: 7084
Don't use length xs > 0
(in sig
); it needlessly counts the length of xs
when all you really wanted to know is whether it's empty. Use null xs
to check for a non-empty list:
...
where sig | null xs = ... -- Empty case
| otherwise = ... -- Non-empty case
or add an argument to sig
and pattern match:
...
where sig (y:ys) = ...
sig [] = ...
Although Nathan Sanders' advice to replace the whole recursive thing with intercalate
is excellent and makes this a moot point.
You're also identifying the type by passing the whole "var:type"
string into type
, so it is testing
"string" `isInfixOf` "name:string"
etc.
You could use break
or span
instead of takeWhile
to separate the name and type earlier:
create_fields (x:xs) = xname ++ type xtype ++ sig
where
(xname, _:xtype) = break (==':') x
sig = ...
and then type
can compare for string equality, or look up values using a Map
.
A quick explanation of that use of break
:
break (==':') "name:string" == ("name", ":string")
Then when binding
(xname, _:xtype) to ("name", ":string"),
xname -> "name"
_ -> ':' (discarded)
xtype -> "string"
Upvotes: 5
Reputation: 18389
I think you understand how to write functional code already. Here are some small style notes:
create_table
, cabo
and final
are not used.Data.List.intercalate "," (map create_field xs)
. Then create_field x
can just be takeWhile (/=':') x ++ type x
Like so:
types = Data.Map.fromList [("string", "CHARACTER VARYING")
,("integer", "INTEGER")
-- etc
]
Then type
can be Data.Maybe.fromMaybe "" (Data.Map.lookup x types)
main
up front. (This is personal preference though)Just say
main = do
args <- getArgs
case args of
[] -> ...
putStrLn
. In the first call, the argument wouldn't require parentheses anyway, and in the second, you supply the parentheses. Alternatively, you could keep the second dollar and drop the parentheses.Upvotes: 6