Avoiding primitive obsession in Haskell - haskell

From http://learnyouahaskell.com/making-our-own-types-and-typeclasses
data Person = Person { name :: String
, age :: Int
} deriving (Show)
In a real application, using primitives like String and Int for name and age would constitue primitive obsession, a code smell. (also obviously Date born is preferable to Int age but let's ignore that) Instead, one would prefer something like
newtype Person = Person { name :: Name
, age :: Age
} deriving (Show)
In an OO language this would look something like
class Person {
Name name;
Age age;
Person(Name name, Age age){
if (name == null || age == null)
throw IllegalArgumentException();
this.name = name;
this.age = age;
}
}
class Name extends String {
Name(String name){
if (name == null || name.isEmpty() || name.length() > 100)
throw IllegalArgumentException();
super(name);
}
}
class Age extends Integer {
Age(Integer age){
if (age == null || age < 0)
throw IllegalArgumentException();
super(age);
}
}
But how is the same achieved in idiomatic, best practice Haskell?

Make Name abstract and provide a smart constructor. This means that you do not export the Name data constructor, and provide a Maybe-returning constructor instead:
module Data.Name
( Name -- note: only export type, not data constructor
, fromString
, toString
) where
newtype Name = Name String
fromString :: String -> Maybe Name
fromString n | null n = Nothing
| length n > 100 = Nothing
| otherwise = Just (Name n)
toString :: Name -> String
toString (Name n) = n
It is now impossible to construct a Name value of the wrong length outside of this module.
For Age, you could do the same thing, or use a type from Data.Word, or use the following inefficient but guaranteed non-negative representation:
data Age = Zero | PlusOne Age

This may be code smell in some languages, but it's not usually considered one in Haskell. You have to choose a concrete representation of a name and birth date somewhere, and the datatype declaration of Person is probably the best place to do it. In Haskell, the usual way to keep other code from depending on the name representation would be to make Person abstract. Instead of exposing the Person constructor, expose functions for creating, modifying, and inspecting Person values.

Related

Kotlin getString() exepects an Int? Why?

I'm going through https://developer.android.com/codelabs/basic-android-kotlin-training-project-lemonade. Here, while trying to make "else" do nothing, I've stumbled upon "Type mismatch" errors.
Regarding setting the view elements, this is what I've come up with so far:
private fun setViewElements() {
val textAction: TextView = findViewById(R.id.text_action)
val text = when (lemonadeState) {
SELECT -> R.string.lemon_select
SQUEEZE -> R.string.lemon_squeeze
DRINK -> R.string.lemon_drink
RESTART -> R.string.lemon_empty_glass
else -> null
}
textAction.text = getString(text)
I've tried all workarounds for "doing nothing": {}, Unit, null, empty string (and even data types like String, Int, Double...) but all I get is a "Type mismatch: inferred type is Any but Int was expected" (or "...inferred type is Int?...") error. So much so that an Int does makes the error disapear, as in:
...
else -> 0
According to the docs, both versions of getString(), single and double paramater, work stritcly with strings: parameter(s) and return type. So why on Earth is it saying this function expects an Int?
Also, writing text as an instance of Int (text: Int), doesn't affect anything, meaning it is in fact an Int. I am missing something big here: aren't those R.string.<name> supposed to be strings?
Btw, I did try this:
private fun setViewElements() {
val textAction: TextView = findViewById(R.id.text_action)
val text = when (lemonadeState) {
SELECT -> R.string.lemon_select
SQUEEZE -> R.string.lemon_squeeze
DRINK -> R.string.lemon_drink
else -> R.string.lemon_empty_glass
}
textAction.text = getString(text)
which is errorless and looks better. However, I wanted to keep the specificity, if possible (and it doesn't answer my question).
As the comments pointed out, getString takes a Int, which is a resource ID. This identifies one of the strings you have written in your XML files. This way, you can easily have e.g. multiple versions of the string, one for each localisation for your app, and getString will figure out which string to use based on the device locale. R.string.lemon_select, R.string.lemon_squeeze etc are those identifiers.
One way to do nothing, is to return just evaluate to null - you just have to handle the null value yourself. This will cause text to be of type Int?.
val text = when (lemonadeState) {
SELECT -> R.string.lemon_select
SQUEEZE -> R.string.lemon_squeeze
DRINK -> R.string.lemon_drink
RESTART -> R.string.lemon_empty_glass
else -> null
}
if (text != null) {
textAction.text = getString(text)
}
From your last code snippet though, it seems like you want to set it to lemon_empty_glass when the else branch is reached. If that is the case, then you can do:
val text = when (lemonadeState) {
SELECT -> R.string.lemon_select
SQUEEZE -> R.string.lemon_squeeze
DRINK -> R.string.lemon_drink
RESTART -> R.string.lemon_empty_glass
else -> null
}
textAction.text = getString(text ?: R.string.lemon_empty_glass)

How can I convert a generic datatype in C#?

See the method below [Not Working as expected].I want double or int as method parameters and return as any data type [in my case return as int,double or string].
Requirement:
if the double or int value is zero then return an empty string else the real int or double value. I don't want to use dynamic as return type
private static T CheckZero<T>( dynamic val ) {
return Math.Sign(val) == 0 ? (T)Convert.ChangeType(string.Empty, typeof(string)) : val;
}

Taking a string from a user and checking it against a "Password" variable

I am trying to create a program that takes a string from a user and checks it against a "password" variable. If the password is equal it prints "valid" if not then "invalid"
Here is what I have so far, it looks correct to me, but it apparently isn't.
import java.util.*;
class WS6Q5 {
public static void main (String[] args){
Scanner in=new Scanner(System.in);
String s =" ";
int x = 123;
System.out.println("Please type in the Password");
s=in.nextLine();
if (s.length()==x){
System.out.println("Access Granted");
}
else if (s.length()!=x){
System.out.println("Invalid");
}
}
As one might expect, s.length() gives you the length, in characters, of string s. Your variable x is set to the integer (not string!) value 123. So your if-statement is seeing whether x is 123 characters long. Probably not what you want!
You probably want x to be a string. String x = "123".
You want to compare the value of x to the value of s. Look at String.equals() (http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#equals(java.lang.Object))

How do I use groovy's AS keyword

This may be a duplicate but "as" is an INCREDABLY hard keyword to google, even S.O. ignores "as" as part of query.
So I'm wondering how to implement a class that supports "as" reflexively. For an example class:
class X {
private val
public X(def v) {
val=v
}
public asType(Class c) {
if (c == Integer.class)
return val as Integer
if(c == String.class)
return val as String
}
}
This allows something like:
new X(3) as String
to work, but doesn't help with:
3 as X
I probably have to attach/modify the "asType" on String and Integer somehow, but I feel any changes like this should be confined to the "X" class... Can the X class either implement a method like:
X fromObject(object)
or somehow modify the String/Integer class from within X. This seems tough since it won't execute any code in X until X is actually used... what if my first usage of X is "3 as X", will X get a chance to override Integer's asType before Groovy tries to call is?
As you say, it's not going to be easy to change the asType method for Integer to accept X as a new type of transformation (especially without destroying the existing functionality).
The best I can think of is to do:
Integer.metaClass.toX = { -> new X( delegate ) }
And then you can call:
3.toX()
I can't think how 3 as X could be done -- as you say, the other way; new X('3') as Integer is relatively easy.
Actually, you can do this:
// Get a handle on the old `asType` method for Integer
def oldAsType = Integer.metaClass.getMetaMethod( "asType", [Class] as Class[] )
// Then write our own
Integer.metaClass.asType = { Class c ->
if( c == X ) {
new X( delegate )
}
else {
// if it's not an X, call the original
oldAsType.invoke( delegate, c )
}
}
3 as X
This keeps the functionality out of the Integer type, and minimizes scope of the effect (which is good or bad depending on what you're looking for).
This category will apply asType from the Integer side.
class IntegerCategory {
static Object asType(Integer inty, Class c) {
if(c == X) return new X(inty)
else return inty.asType(c)
}
}
use (IntegerCategory) {
(3 as X) instanceof X
}

optional arguments in haskell

I have declared my own type:
data Book = Bookinfo {
bookId :: Int,
title :: String
} deriving(Show)
and now:
x = Bookinfo
it is all ok, valid statement
but making bookId x throws an error.
If I would be able to handle errors in Haskell that would be ok but right now I cant do this So Im curious how to make not specified values of fields take default value, and what exactly value is there when I'm not giving vcalues of fields in construcotr ?
thanks for help
-- this one is not a Book but actually a function that can make one:
alternativeCtrFunc = Bookinfo
defaultBook = Bookinfo { bookId = 3, title = "Welcome to the Monkey House" }
x = defaultBook
y = defaultBook { bookId = 7 }
z = defaultBook { title = "The Cider House Rules" }

Resources