{"action":"created","issue":{"url":"https://api.github.com/repos/fsharp/fslang-suggestions/issues/577","repository_url":"https://api.github.com/repos/fsharp/fslang-suggestions","labels_url":"https://api.github.com/repos/fsharp/fslang-suggestions/issues/577/labels{/name}","comments_url":"https://api.github.com/repos/fsharp/fslang-suggestions/issues/577/comments","events_url":"https://api.github.com/repos/fsharp/fslang-suggestions/issues/577/events","html_url":"https://github.com/fsharp/fslang-suggestions/issues/577","id":233265507,"node_id":"MDU6SXNzdWUyMzMyNjU1MDc=","number":577,"title":"Add non-nullable instantiations of nullable types, and interop with proposed C# 8.0 non-nullable reference types","user":{"login":"0x53A","id":4236651,"node_id":"MDQ6VXNlcjQyMzY2NTE=","avatar_url":"https://avatars3.githubusercontent.com/u/4236651?v=4","gravatar_id":"","url":"https://api.github.com/users/0x53A","html_url":"https://github.com/0x53A","followers_url":"https://api.github.com/users/0x53A/followers","following_url":"https://api.github.com/users/0x53A/following{/other_user}","gists_url":"https://api.github.com/users/0x53A/gists{/gist_id}","starred_url":"https://api.github.com/users/0x53A/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/0x53A/subscriptions","organizations_url":"https://api.github.com/users/0x53A/orgs","repos_url":"https://api.github.com/users/0x53A/repos","events_url":"https://api.github.com/users/0x53A/events{/privacy}","received_events_url":"https://api.github.com/users/0x53A/received_events","type":"User","site_admin":false},"labels":[{"id":552474499,"node_id":"MDU6TGFiZWw1NTI0NzQ0OTk=","url":"https://api.github.com/repos/fsharp/fslang-suggestions/labels/area:%20interop","name":"area: interop","color":"fef2c0","default":false},{"id":854259695,"node_id":"MDU6TGFiZWw4NTQyNTk2OTU=","url":"https://api.github.com/repos/fsharp/fslang-suggestions/labels/proposed-priority","name":"proposed-priority","color":"56b8d3","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":21,"created_at":"2017-06-02T18:37:50Z","updated_at":"2018-06-25T17:19:51Z","closed_at":null,"author_association":"CONTRIBUTOR","body":"[Updated description by @dsyme]\r\n\r\nC# 8.0 is likely to ship with [a feature where new C# projects opt-in by default to making reference types \"without-null\" by default](https://github.com/dotnet/csharplang/issues/36)), with explicit annotations for reference types \"with-null\".  The corresponding metadata annotations produced by this feature are likely to start to be used by .NET Framework libraries.\r\n\r\nF#-defined types are \"without-null\" by default, however .NET-defined types like `string` and `array` and any other types defined in .NET libraries are \"with-null\" by default.    The suggestion is to make new F# 5.0 projects opt-in to having .NET reference types be \"without-null\" by default when mentioned in F# code, and to interoperate with .NET metadata produced by C# 8.0 indicating when types are \"with-null\".\r\n\r\n### Terminology and working assumptions\r\n\r\nThese terms are used interchangeably:\r\n\r\n* \"null as a abnormal value\", \"string without null\", \"non-nullable string\"\r\n\r\nLikewise\r\n* \"null as a normal value\", \"string with null\", \"nullable string\"\r\n\r\nFor the purposes of discussion we will use `string | null` as the syntax for types explicitly annotated to be \"with null\".  You will also see `string?` in some samples\r\n\r\nWe will assume this feature is for F# 5.0 and is activated by a \"/langlevel:5.0\" switch that is on by default for new projects.\r\n\r\n### Proposed Design Principles\r\n\r\nWe are at the stage of trying to clarify the set of design principles to guide this feature:\r\n\r\n1. We should aim that F# should remain \"almost\" as simple to use as it is today. Indeed, the aim should be that the experience of using F# **as a whole** is simpler, because the possibility of nulls flowing in from .NET code for types like `string` is reduced and better tracked.\r\n\r\n2. The value for F# here is primarily in flowing non-nullable annotations into F# code from .NET libraries, and vice-versa, and in allowing the F# programmer to be explicit about the non-nullability of .NET types.\r\n\r\n3. Adding with-null/without-null annotations should not be part of routine F# programming\r\n\r\n4. There is a known risk of [\"extra information overload\"](https://github.com/fsharp/fslang-suggestions/issues/577#issuecomment-399994981) in some tooling, e.g. tooltips.  Nullability annotations/information may need to be suppressed and simplified in some types shown in output in routine F# programming. There is discussion about how this would be tuned in practice\r\n\r\n5. F# users should primarily only experience/see this feature when interoperating with .NET libraries (the latter should be rarely needed)\r\n\r\n6. The feature should produce warnings only, not hard errors\r\n\r\n7. The feature is backwards compatible, but only in the sense all existing F# projects compile without warning by default.  Placing F# 4.x code into F# 5.0 projects may give warnings.\r\n\r\n8. F# non-nullness is reasonably \"strong and trustworthy\" today for F#-defined types and routine F# coding. This includes the explicit use of `option` types to represent the absence of information.  The feature should not lead to a weakening of this trust nor a change in F# methodology that leads to lower levels of safety.\r\n\r\n### Notes from original posting\r\n\r\n_Note that there are a few related proposals (see below), but I couldn't find an exact match in this repo._\r\n\r\nOne reason I am adding this proposal is that I have lately been working in a mixed C# / F# solution, where I DO need to work with null more often than I like.\r\n\r\nThe other reason is that we should be aware of the parallel proposal for C# (https://github.com/dotnet/csharplang/issues/36).\r\n\r\nMy main question is: __Is there a minimal implementation for F#, that eases working with C# types NOW, without blocking adoption of future C# evolutions?__\r\n\r\n### The existing way of approaching this problem in F# is ...\r\n\r\nTypes declared in F# are non-nullable by default. You can either make them nullable with ``AllowNullLiteralAttribute``, or wrap them in an Option<'T>.\r\n\r\nTypes declared either in C#, or in a third-party F# source with ``AllowNullLiteralAttribute`` are always nullable, so in theory you would need to deal with null for every instance. In practice, this is often ignored and may or (often even worse) may not fail with a null-reference exception.\r\n\r\n### I propose we ...\r\n\r\nI propose we add a type modifier to declare that this instance shall never be null.\r\nThis will be most useful in a function parameter.\r\n\r\nBecause I do not want to discuss the actual form of that modifier (attribute, keyword, etc), I will use ``'T foo`` as meaning ``non-nullable 'T``, similar to ``'T option``.\r\n\r\n### Example:\r\n\r\n```F#\r\nlet stringLength (s:string foo) = s.Length\r\n```\r\n\r\nCalling this method with a vanilla string instance would produce a hard error.\r\n\r\nHow can a nullable type be converted to a non-nullable? \r\n\r\nThere should be at least a limited form of flow analysis.\r\n\r\nTwo examples would be if-branches and pattern matching:\r\n\r\n```F#\r\n\r\nlet s = ... // vanilla (nullable) string\r\n\r\n(* 1 - pattern matching *)\r\n\r\nmatch s with\r\n| null -> 0\r\n| s -> stringLength s\r\n\r\n// Note that this does not change the original variable s - the new s shadows the old s. I can also use a different variable name:\r\n\r\nmatch s with\r\n| null -> 0\r\n| s2 ->\r\n    // this fails:\r\n    // stringLength s\r\n    // this works:\r\n    stringLength s2\r\n\r\n\r\n\r\n(* 2 - explicit null-checking *)\r\n\r\nif s = null then\r\n    ()\r\nelse\r\n    // now the compiler knows s is non-nullable\r\n    stringLength s\r\n```\r\n\r\nThere probably also needs to be a shorthand to force a cast, similar to ``Option.Value``, which will throw at runtime if the option is null.\r\n\r\n## Runtime behavior\r\n\r\nThe types are erased to attributes.\r\nYou can't overload between nullable and non-nullable (may in combination with ``inline``?)\r\nWhen a non-null type modification is used in a function parameter, the compiler should insert a null-check at the beginning of the method.\r\nThe compiler should also add a null-check for all hard casts.\r\n\r\n## Pros and Cons \r\n\r\nThe advantages of making this adjustment to F# are ...\r\n\r\nstronger typing.\r\n\r\nThe disadvantages of making this adjustment to F# are ...\r\n\r\nyet another erased type modifier, like UOM, aliases.\r\n\r\n## Extra information\r\n\r\nEstimated cost (XS, S, M, L, XL, XXL): L\r\n\r\nRelated suggestions: (put links to related suggestions here)\r\n\r\nhttps://github.com/fsharp/fslang-suggestions/issues/552: ``Flow based null check analysis for [<AllowNullLiteralAttribute>] types and alike``\r\n\r\n## Affidavit (must be submitted)\r\n\r\nPlease tick this by placing a cross in the box:\r\n* [x] This is not a question (e.g. like one you might ask on [stackoverflow](http://stackoverflow.com)) and I have searched stackoverflow for discussions of this issue\r\n* [x] I have [searched both open and closed suggestions on this site](http://github.com/fsharp/fslang-suggestions/issues) and believe this is not a duplicate\r\n* [x] This is not something which has obviously \"already been decided\" in previous versions of F#.  If you're questioning a fundamental design decision that has obviously already been taken (e.g. \"Make F# untyped\") then please don't submit it.\r\n\r\nPlease tick all that apply:\r\n* [ ] This is not a breaking change to the F# language design\r\n* [x] I would be willing to help implement and/or test this\r\n* [ ] I or my company would be willing to help crowdfund F# Software Foundation members to work on this\r\n\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/fsharp/fslang-suggestions/issues/comments/400029071","html_url":"https://github.com/fsharp/fslang-suggestions/issues/577#issuecomment-400029071","issue_url":"https://api.github.com/repos/fsharp/fslang-suggestions/issues/577","id":400029071,"node_id":"MDEyOklzc3VlQ29tbWVudDQwMDAyOTA3MQ==","user":{"login":"cartermp","id":6309070,"node_id":"MDQ6VXNlcjYzMDkwNzA=","avatar_url":"https://avatars0.githubusercontent.com/u/6309070?v=4","gravatar_id":"","url":"https://api.github.com/users/cartermp","html_url":"https://github.com/cartermp","followers_url":"https://api.github.com/users/cartermp/followers","following_url":"https://api.github.com/users/cartermp/following{/other_user}","gists_url":"https://api.github.com/users/cartermp/gists{/gist_id}","starred_url":"https://api.github.com/users/cartermp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cartermp/subscriptions","organizations_url":"https://api.github.com/users/cartermp/orgs","repos_url":"https://api.github.com/users/cartermp/repos","events_url":"https://api.github.com/users/cartermp/events{/privacy}","received_events_url":"https://api.github.com/users/cartermp/received_events","type":"User","site_admin":false},"created_at":"2018-06-25T17:19:50Z","updated_at":"2018-06-25T17:19:50Z","author_association":"MEMBER","body":"Re: BCL\r\n\r\nAfter talking with @terrajobst, the plan is to do something where types are annotated as nullable if the input type is valid or the return type is null. However, there are numerous places where they just throw an exception if an input is null, or they never return null explicitly. Thus, the current plan does not involve blanket annotations for everything.\r\n\r\nI'm not sure of the vehicle, but because it's unlikely that we'll be updating older .NET Framework versions, I suspect that some sort of shim will be involved.\r\n\r\nBut I think the overall goal is to make this feature also \"work\" on the BCL as much as they can, since it's arguably the most valuable place to have it \"enabled\"."}}
{"actor":{"display_login":"cartermp"}}