Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrong semantic of newtype #351

Open
yuxuanchiadm opened this issue Mar 26, 2018 · 3 comments
Open

Wrong semantic of newtype #351

yuxuanchiadm opened this issue Mar 26, 2018 · 3 comments

Comments

@yuxuanchiadm
Copy link
Contributor

Consider following code:

newtype Bar = Bar { getBar :: String }

foo :: Bar -> ()
foo (Bar _) = ()

main = println (foo undefined)

What is the result of this program? Since newtype is perfect isomorphism. Code above should identical to:

foo :: String -> ()
foo _ = ()

main = println (foo undefined)

Which should display "()" in console. But program instead simply crashed. Because definition of foo generated code final public static short foo(final String/*<Character>*/ arg$1). Which is not lazy. And compiler gererates code foo(PreludeBase.<String/*<Character>*/>undefined().call()). So we got ⊥.

@yuxuanchiadm
Copy link
Contributor Author

Related to #293.

@Ingo60
Copy link
Member

Ingo60 commented Mar 26, 2018

You are right, but there is one reason for doing it this way, even if this might be different in Haskell. And this is that

foo (Bar _) = ()

looks exactly like

foo (Just _) = ()

The behavior of the Frege compiler is so that just by looking at that line you'll know that the argument is strict. You don't have to know that Bar is a newtype constructor and Just is a sum type constructor, and that there are supposed to be subtle semantic differences.

All you need to know is: Ohh, this pattern deconstructs something, hence it will be strict.

@yuxuanchiadm
Copy link
Contributor Author

yuxuanchiadm commented Mar 26, 2018

I still think pattern match on constructor of newtype designed as strict will not be reasonable. It always will be wired in some case. Consider this:

newtype Bar = Bar { getBar :: String }

foo :: Bar -> ()
foo (Bar _) = ()

main = println (foo (Bar undefined))

Now does it feel the pattern match should always don't fail? The pattern should only strict on constructor right? We don't care about what value it contains. But we still get ⊥ if we deal pattern match as strict.

The best way to think about newtype imo is: It just synonym of the type. We use constructor, pattern match every time when we want to convert between them. The convert should be prefect. So we are not really create new data type or pattern match on data type. We just want to change the name of type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants