This commit is contained in:
William Blum 2019-08-08 17:18:50 -07:00 коммит произвёл William Blum
Родитель f11edb831e
Коммит 204f7a93f6
11 изменённых файлов: 516 добавлений и 64 удалений

Просмотреть файл

@ -36,13 +36,13 @@ let newHttpClient (auth:Auth.ADAuthenticationMethod) =
type AppInsightsTelemetryGetter(httpClient:HttpClient, subscription: Subscription, resourceGroup: string, appInsightsDeployment: string) =
// App Insights API team asked to add this header to the requests, this way they can easier debug and diagnose issues that might happen on the API controller side
let [<Literal>] msClientIdHeader = "x-ms-client-request-id"
let [<Literal>] MsClientIdHeader = "x-ms-client-request-id"
let getAppInsights (url: string) =
async {
let requestId = Guid.NewGuid()
httpClient.DefaultRequestHeaders.Remove(msClientIdHeader) |> ignore
httpClient.DefaultRequestHeaders.Add(msClientIdHeader, requestId.ToString())
httpClient.DefaultRequestHeaders.Remove(MsClientIdHeader) |> ignore
httpClient.DefaultRequestHeaders.Add(MsClientIdHeader, requestId.ToString())
//This try/catch a work around for the bug in dotnet core where we get OperationCancelledException raised on a timeout (https://github.com/dotnet/corefx/issues/20296)
//IMPORTANT: this work item will break the timeout handling after it gets fixed.

Просмотреть файл

@ -162,14 +162,14 @@ let setAccessPolicy
&& permissionsToSecrets.IsNone
&& permissionsToCertificates.IsNone
&& permissionsToStorage.IsNone then
raise <| new System.ArgumentException("No permissions specified")
raise <| System.ArgumentException("No permissions specified")
use c = new Microsoft.Azure.Management.KeyVault.KeyVaultManagementClient(
Microsoft.Rest.TokenCredentials(authenticationToken),
SubscriptionId = subscriptionId)
if isNull vault then
raise <| new System.NullReferenceException("Vault parameter is null: vault")
raise <| System.NullReferenceException("Vault parameter is null: vault")
let emptyPermissions (s:(string []) option) =
s.IsNone || Seq.isEmpty s.Value
@ -302,7 +302,7 @@ let trySetAccessPolicy
&& permissionsToSecrets.IsNone
&& permissionsToCertificates.IsNone
&& permissionsToStorage.IsNone then
raise <| new System.ArgumentException("No permissions specified")
raise <| System.ArgumentException("No permissions specified")
use c = new Microsoft.Azure.Management.KeyVault.KeyVaultManagementClient(
Microsoft.Rest.TokenCredentials(authenticationToken),

Просмотреть файл

@ -52,7 +52,7 @@ let registerPortRedirections tags (c:Microsoft.Azure.Management.Network.INetwork
// Create port redirect rule in load balancer
let addRule (assignedPublicPort, (localPort, name, protocol)) =
let rule = new Models.InboundNatRule(
let rule = Models.InboundNatRule(
protocol = protocol,
backendPort = System.Nullable localPort,
frontendIPConfiguration = frontendIp,

Просмотреть файл

@ -54,7 +54,7 @@ let exists2 (azure:Auth.Subscription) groupName =
/// Create an Azure Resource Group (does not fail if it already exists)
let create (c:Context.InfrastructureContext) groupName location tagsMap =
async {
TraceTags.info "Creating the resource group" (c.tags @ ["groupName", groupName])
let resourceGroup =
ResourceGroup(
@ -159,7 +159,7 @@ let generateRandomAzureDeploymentName (prefix:string) =
// one char for the underscode and another one for at least one random character
let minimalSuffixLength = 2
if prefix.Length + minimalSuffixLength > MaxAzureDeploymentNameLength then
raise (new System.ArgumentOutOfRangeException(sprintf "The prefix length should me be less than %d" MaxAzureDeploymentNameLength))
raise (System.ArgumentOutOfRangeException(sprintf "The prefix length should me be less than %d" MaxAzureDeploymentNameLength))
let charList = "01234567891abcdefghijklmnopqrstuvwxyz"
let generator = Random(int System.DateTime.UtcNow.Ticks)

Просмотреть файл

@ -17,7 +17,7 @@ let mutable public tryGetConfigValue =
| v -> Some v
/// Read a configuration value
let public getConfigValue key =
let public getConfigValue key =
tryGetConfigValue key |> Option.orDo (fun () -> invalidOp (sprintf "Configuration key %s missing from config file" key))
/// Set a configuration value
@ -33,18 +33,18 @@ let public getConfigArray name =
/// Use a user-specified .config file
let public loadCustomConfig filePath =
let configFileMap = new ExeConfigurationFileMap(ExeConfigFilename = filePath)
let configFileMap = ExeConfigurationFileMap(ExeConfigFilename = filePath)
let config = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None)
if config.AppSettings = null || config.AppSettings.Settings = null then
if isNull config.AppSettings || isNull config.AppSettings.Settings then
invalidOp (sprintf "Settings missing from config file: %s" filePath)
let settings = config.AppSettings.Settings
tryGetConfigValue <- fun (key:string) ->
tryGetConfigValue <- fun (key:string) ->
match settings.[key] with
| null -> None
| v -> Some v.Value
setConfigValue <- fun (key:string) value ->
if settings.[key] <> null then
setConfigValue <- fun (key:string) value ->
if not <| isNull settings.[key] then
settings.Remove(key)
settings.Add(key, value)
config

Просмотреть файл

@ -51,7 +51,7 @@ module Seq =
open System.Collections
let noReset() = raise (new System.NotSupportedException("Reset not supported on this enumerable"))
let noReset() = raise (System.NotSupportedException("Reset not supported on this enumerable"))
let IEnumerator_cast (e : IEnumerator) : IEnumerator<'T> =
{

Просмотреть файл

@ -26,7 +26,7 @@ let public getExistingDir path =
/// Return the size in bytes of a file
let fileLength filePath =
let f = new System.IO.FileInfo(filePath)
let f = System.IO.FileInfo(filePath)
f.Length
/// Append a line to a text file
@ -257,14 +257,14 @@ let getIniValue (file:string) textToSearchFor =
module private Constants =
let [<Literal>] FILE_ALREADY_EXISTS = 0x80070050
/// Create a new file if it does not already exist and atomically write to it using the specified FileStream function.
/// Create a new file if it does not already exist and atomically write to it using the specified FileStream function.
/// If the file already exists then do nothing and return None.
let asyncTryCreateFile filePath (f: System.IO.FileStream -> Async<'a>) =
async {
if System.IO.File.Exists(filePath) then
return None
else
let fs =
let fs =
try
Some (new System.IO.FileStream(filePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None))
with
@ -272,9 +272,9 @@ let asyncTryCreateFile filePath (f: System.IO.FileStream -> Async<'a>) =
match fs with
| Some fileStream ->
use stream = fileStream
use stream = fileStream
let! result = f stream
return Some result
| None ->
| None ->
return None
}

Просмотреть файл

@ -364,11 +364,11 @@ module Client =
clientHandler.ClientCertificates.Add(certificate) |> ignore
new HttpClient(clientHandler,
BaseAddress = new Uri(httpOptions.ServerEndpoint),
BaseAddress = Uri(httpOptions.ServerEndpoint),
Timeout = httpOptions.Timeout)
| None ->
new HttpClient(
BaseAddress = new Uri(httpOptions.ServerEndpoint),
BaseAddress = Uri(httpOptions.ServerEndpoint),
Timeout = httpOptions.Timeout
)
client.DefaultRequestHeaders.ConnectionClose <- (System.Nullable<bool> true)
@ -381,7 +381,7 @@ module Client =
client.DefaultRequestHeaders.Add(Constants.CorrelationIdHeader, correlationId)
client.DefaultRequestHeaders.Accept.Clear()
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(Constants.JsonMediaType))
client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue(Constants.JsonMediaType))
client
module Methods =

Просмотреть файл

@ -21,7 +21,7 @@ let getSection (sectionName:string) (configuration:Configuration) =
configuration.TryFind(sectionName) |> Option.defaultValue Map.empty
/// True if the section name was found Ini configuration
let hasSection (sectionName:string) (configuration:Configuration) : bool =
let hasSection (sectionName:string) (configuration:Configuration) : bool =
configuration.TryFind(sectionName) |> Option.isSome
/// Tries to get a section from an Configuration
@ -29,15 +29,15 @@ let tryGetSection (sectionName:string) (configuration:Configuration) =
configuration.TryFind(sectionName)
/// True if the named parameter exists in the section
let hasParameter (parameterName:string) (section:Section) : bool =
let hasParameter (parameterName:string) (section:Section) : bool =
section.TryFind(parameterName) |> Option.isSome
/// Tries to get the value of a parameter in the section
let tryGetValue (parameterName:string) (section:Section) =
let tryGetValue (parameterName:string) (section:Section) =
section.TryFind(parameterName)
/// Gets the value of a parameter in the section, fail if the parameter name is not found
let getValueOrFail (parameterName:string) (section:Section) =
let getValueOrFail (parameterName:string) (section:Section) =
match section.TryFind(parameterName) with
| Some value ->
value
@ -45,32 +45,32 @@ let getValueOrFail (parameterName:string) (section:Section) =
failwith (sprintf "ini parameter %s not found" parameterName)
/// Gets the value of a parameter in the section (returns the specified default value if not found)
let getValueOrDefault (parameterName:string) (defaultValue:string) (section:Section) =
let getValueOrDefault (parameterName:string) (defaultValue:string) (section:Section) =
section.TryFind(parameterName) |> Option.defaultValue defaultValue
// True if the parameter exists in the section
let hasParameterInSection (sectionName:string) (parameterName:string) (configuration:Configuration) =
configuration
|> getSection sectionName
configuration
|> getSection sectionName
|> hasParameter parameterName
/// Tries to get the value of a parameter in the named section
let tryGetValueFromSection (sectionName:string) (parameterName:string) (configuration:Configuration) =
configuration
|> getSection sectionName
configuration
|> getSection sectionName
|> tryGetValue parameterName
/// Gets the value of a parameter in the named section (returns the default value if not found)
let getValueFromSectionOrDefault (sectionName:string) (parameterName:string) (defaultValue:string) (configuration:Configuration) =
configuration
|> tryGetValueFromSection sectionName parameterName
configuration
|> tryGetValueFromSection sectionName parameterName
|> Option.defaultValue defaultValue
/// Regex parser for INI configuration file syntax
let private matcher = new Regex("\s*\[(?<section>[^\]]+?)\s*]|^;(?<comment>.*)$|\s*(?<name>[^;=]+?)\s*=\s*(?<value>.*?)\s*$|(?<whitespace>\s*)", RegexOptions.Compiled|||RegexOptions.Singleline)
let private matcher = Regex("\s*\[(?<section>[^\]]+?)\s*]|^;(?<comment>.*)$|\s*(?<name>[^;=]+?)\s*=\s*(?<value>.*?)\s*$|(?<whitespace>\s*)", RegexOptions.Compiled|||RegexOptions.Singleline)
let private (|Section|NameValue|Whitespace|Comment|Error|) (line:string) =
match matcher.Match(line) with
match matcher.Match(line) with
| matchResult when matchResult.Success && matchResult.Groups.["section"].Success ->
Section matchResult.Groups.["section"].Value
| matchResult when matchResult.Success && matchResult.Groups.["name"].Success && matchResult.Groups.["value"].Success ->
@ -88,15 +88,15 @@ let parseConfigurationFromLines (lines:string seq) (sourceContext:string) : Conf
lines
|> Seq.takeWhile(fun line -> match line with Section _ -> false | _ -> true)
let configuration:Configuration =
lines |> Seq.foldi(fun lineNumber sectionMap line ->
let configuration:Configuration =
lines |> Seq.foldi(fun lineNumber sectionMap line ->
match line with
| Section sectionName ->
let section:Section =
lines
let section:Section =
lines
|> Seq.skip (lineNumber + 1)
|> Seq.takeWhile(fun line -> match line with Section name -> false | _ -> true )
|> Seq.foldi(fun sectionLineNumber section line ->
|> Seq.foldi(fun sectionLineNumber section line ->
match line with
| NameValue(name,value) ->
section.Add(name,value)
@ -116,9 +116,9 @@ let parseConfigurationFromLines (lines:string seq) (sourceContext:string) : Conf
if Seq.isEmpty leadingNameValueLinesWithNoSection then
configuration
else
let unnamedSection:Section =
let unnamedSection:Section =
leadingNameValueLinesWithNoSection
|> Seq.foldi(fun lineNumber section line ->
|> Seq.foldi(fun lineNumber section line ->
match line with
| NameValue(name,value) ->
section.Add(name,value)
@ -132,26 +132,26 @@ let parseConfigurationFromLines (lines:string seq) (sourceContext:string) : Conf
/// Read and parse an ini file and return the IniFile type containing of all sections which, in turn, contains a dictionary of all name/value pairs for that section
let readConfigurationFile (path:string) =
parseConfigurationFromLines (System.IO.File.ReadAllLines(path)) path
/// Convert to a list of ini text lines
let configurationToLines (configuration:Configuration) =
let unnamedSection =
let unnamedSection =
if configuration.ContainsKey(UnnamedSectionName) then
configuration.Item(UnnamedSectionName)
|> Seq.map(fun nameValuePair -> sprintf "%s=%s" nameValuePair.Key nameValuePair.Value)
configuration.Item(UnnamedSectionName)
|> Seq.map(fun nameValuePair -> sprintf "%s=%s" nameValuePair.Key nameValuePair.Value)
|> Seq.toList
else
else
[]
let namedSections =
configuration
|> Seq.sortBy(fun nvp -> nvp.Key)
|> Seq.filter(fun nvp -> nvp.Key <> UnnamedSectionName)
|> Seq.map(fun nameValuePair ->
let namedSections =
configuration
|> Seq.sortBy(fun nvp -> nvp.Key)
|> Seq.filter(fun nvp -> nvp.Key <> UnnamedSectionName)
|> Seq.map(fun nameValuePair ->
sprintf "[%s]" nameValuePair.Key
::
(nameValuePair.Value
|> Seq.sortBy(fun nvp -> nvp.Key)
(nameValuePair.Value
|> Seq.sortBy(fun nvp -> nvp.Key)
|> Seq.map(fun nameValuePair -> sprintf "%s=%s" nameValuePair.Key nameValuePair.Value) |> Seq.toList)
)
List.append unnamedSection (List.concat namedSections)
@ -161,8 +161,7 @@ let writeConfigurationFile (configuration:Configuration) (path:string) =
File.WriteAllLines(path, configurationToLines configuration)
/// Converts this IniFile type to a string
let configurationToString configuration =
configurationToLines configuration
|> List.toSeq
let configurationToString configuration =
configurationToLines configuration
|> List.toSeq
|> String.concat System.Environment.NewLine

Просмотреть файл

@ -25,11 +25,11 @@ type CaseInsensitiveComparer() =
/// Extension methods for String
type System.String with
/// Extend the string replace function to allow for StringComparison options to be specified
member this.Replace (oldString:string, newString:string, comparisonType:System.StringComparison): string =
let index = this.IndexOf(oldString, comparisonType)
if index >= 0 then
this.Remove(index, oldString.Length).Replace(oldString, newString, comparisonType).Insert(index, newString)
else
@ -167,7 +167,7 @@ let decodeFromBase64 (base64Encoded:byte[]) =
/////// Implementation of Knuth–Morris–Pratt on Stream
/// Used by kmpTryFindBytesInStream below to compute the backtrack array
let computeKmpBacktrack (searchBytes: uint8[]) =
let computeKmpBacktrack (searchBytes: uint8[]) =
let backtrack = Array.zeroCreate (searchBytes.Length+1)
let rec back b j =
if j >= 0 && b <> searchBytes.[j] then
@ -205,7 +205,7 @@ let kmpFindBytesInStream (findOptions:FindOptions) (stream:System.IO.Stream) (se
let mutable k = 0
let mutable byteRead = stream.ReadByte()
let mutable results = []
while byteRead <> -1 && (results = [] || findOptions = FindAll) do
while byteRead <> -1 && (List.isEmpty results || findOptions = FindAll) do
if searchBytes.[k] = (byteRead |> uint8) then
k <- k + 1
if k = searchBytes.Length then

453
fsharplint.json Normal file
Просмотреть файл

@ -0,0 +1,453 @@
{
"ignoreFiles": [
"assemblyinfo.*"
],
"formatting": {
"typedItemSpacing": {
"enabled": false,
"config": {
"typedItemStyle": "NoSpaces"
}
},
"typePrefixing": {
"enabled": false
},
"unionDefinitionIndentation": {
"enabled": false
},
"moduleDeclSpacing": {
"enabled": false
},
"classMemberSpacing": {
"enabled": false
},
"tupleFormatting": {
"tupleCommaSpacing": {
"enabled": false
},
"tupleIndentation": {
"enabled": false
},
"tupleParentheses": {
"enabled": false
}
},
"patternMatchFormatting": {
"patternMatchClausesOnNewLine": {
"enabled": false
},
"patternMatchOrClausesOnNewLine": {
"enabled": false
},
"patternMatchClauseIndentation": {
"enabled": false
},
"patternMatchExpressionIndentation": {
"enabled": false
}
}
},
"conventions": {
"recursiveAsyncFunction": {
"enabled": false
},
"redundantNewKeyword": {
"enabled": true
},
"nestedStatements": {
"enabled": false,
"config": {
"depth": 8
}
},
"reimplementsFunction": {
"enabled": true
},
"canBeReplacedWithComposition": {
"enabled": true
},
"raiseWithTooManyArgs": {
"raiseWithSingleArgument": {
"enabled": true
},
"nullArgWithSingleArgument": {
"enabled": true
},
"invalidOpWithSingleArgument": {
"enabled": true
},
"invalidArgWithTwoArguments": {
"enabled": true
},
"failwithfWithArgumentsMatchingFormatString": {
"enabled": true
}
},
"sourceLength": {
"maxLinesInLambdaFunction": {
"enabled": false,
"config": {
"maxLines": 7
}
},
"maxLinesInMatchLambdaFunction": {
"enabled": false,
"config": {
"maxLines": 100
}
},
"maxLinesInValue": {
"enabled": false,
"config": {
"maxLines": 100
}
},
"maxLinesInFunction": {
"enabled": false,
"config": {
"maxLines": 100
}
},
"maxLinesInMember": {
"enabled": false,
"config": {
"maxLines": 100
}
},
"maxLinesInConstructor": {
"enabled": false,
"config": {
"maxLines": 100
}
},
"maxLinesInProperty": {
"enabled": false,
"config": {
"maxLines": 70
}
},
"maxLinesInModule": {
"enabled": false,
"config": {
"maxLines": 1000
}
},
"maxLinesInRecord": {
"enabled": false,
"config": {
"maxLines": 500
}
},
"maxLinesInEnum": {
"enabled": false,
"config": {
"maxLines": 500
}
},
"maxLinesInUnion": {
"enabled": false,
"config": {
"maxLines": 500
}
},
"maxLinesInClass": {
"enabled": false,
"config": {
"maxLines": 500
}
}
},
"naming": {
"interfaceNames": {
"enabled": true,
"config": {
"naming": "PascalCase",
"underscores": "None",
"prefix": "I"
}
},
"exceptionNames": {
"enabled": true,
"config": {
"naming": "PascalCase",
"underscores": "None"
}
},
"typeNames": {
"enabled": true,
"config": {
"naming": "PascalCase",
"underscores": "None"
}
},
"recordFieldNames": {
"enabled": false,
"config": {
"naming": "PascalCase",
"underscores": "None"
}
},
"enumCasesNames": {
"enabled": true,
"config": {
"naming": "PascalCase",
"underscores": "None"
}
},
"unionCasesNames": {
"enabled": true,
"config": {
"naming": "PascalCase",
"underscores": "None"
}
},
"moduleNames": {
"enabled": true,
"config": {
"naming": "PascalCase",
"underscores": "None"
}
},
"literalNames": {
"enabled": true,
"config": {
"naming": "PascalCase",
"underscores": "None"
}
},
"namespaceNames": {
"enabled": true,
"config": {
"naming": "PascalCase",
"underscores": "None"
}
},
"memberNames": {
"enabled": true,
"config": {
"underscores": "AllowPrefix"
}
},
"parameterNames": {
"enabled": true,
"config": {
"naming": "CamelCase",
"underscores": "AllowPrefix"
}
},
"measureTypeNames": {
"enabled": true,
"config": {
"underscores": "None"
}
},
"activePatternNames": {
"enabled": true,
"config": {
"naming": "PascalCase",
"underscores": "None"
}
},
"publicValuesNames": {
"enabled": true,
"config": {
"underscores": "AllowPrefix"
}
},
"nonPublicValuesNames": {
"enabled": true,
"config": {
"naming": "CamelCase",
"underscores": "AllowPrefix"
}
}
},
"numberOfItems": {
"maxNumberOfItemsInTuple": {
"enabled": false,
"config": {
"maxItems": 4
}
},
"maxNumberOfFunctionParameters": {
"enabled": false,
"config": {
"maxItems": 5
}
},
"maxNumberOfMembers": {
"enabled": false,
"config": {
"maxItems": 32
}
},
"maxNumberOfBooleanOperatorsInCondition": {
"enabled": false,
"config": {
"maxItems": 4
}
}
},
"binding": {
"favourIgnoreOverLetWild": {
"enabled": true
},
"wildcardNamedWithAsPattern": {
"enabled": true
},
"uselessBinding": {
"enabled": true
},
"tupleOfWildcards": {
"enabled": true
}
}
},
"typography": {
"indentation": {
"enabled": false,
"config": {
"numberOfIndentationSpaces": 4
}
},
"maxCharactersOnLine": {
"enabled": false,
"config": {
"maxCharactersOnLine": 120
}
},
"trailingWhitespaceOnLine": {
"enabled": false,
"config": {
"numberOfSpacesAllowed": 1,
"oneSpaceAllowedAfterOperator": true,
"ignoreBlankLines": true
}
},
"maxLinesInFile": {
"enabled": false,
"config": {
"maxLinesInFile": 1000
}
},
"trailingNewLineInFile": {
"enabled": false
},
"noTabCharacters": {
"enabled": true
}
},
"hints": {
"add": [
"not (a = b) ===> a <> b",
"not (a <> b) ===> a = b",
"not (a > b) ===> a <= b",
"not (a >= b) ===> a < b",
"not (a < b) ===> a >= b",
"not (a <= b) ===> a > b",
"compare x y <> 1 ===> x <= y",
"compare x y = -1 ===> x < y",
"compare x y <> -1 ===> x >= y",
"compare x y = 1 ===> x > y",
"compare x y <= 0 ===> x <= y",
"compare x y < 0 ===> x < y",
"compare x y >= 0 ===> x >= y",
"compare x y > 0 ===> x > y",
"compare x y = 0 ===> x = y",
"compare x y <> 0 ===> x <> y",
"List.head (List.sort x) ===> List.min x",
"List.head (List.sortBy f x) ===> List.minBy f x",
"List.map f (List.map g x) ===> List.map (g >> f) x",
"Array.map f (Array.map g x) ===> Array.map (g >> f) x",
"Seq.map f (Seq.map g x) ===> Seq.map (g >> f) x",
"List.nth x 0 ===> List.head x",
"List.map f (List.replicate n x) ===> List.replicate n (f x)",
"List.rev (List.rev x) ===> x",
"Array.rev (Array.rev x) ===> x",
"List.fold (@) [] x ===> List.concat x",
"List.map id x ===> id x",
"Array.map id x ===> id x",
"Seq.map id x ===> id x",
"(List.length x) = 0 ===> List.isEmpty x",
"(Array.length x) = 0 ===> Array.isEmpty x",
"(Seq.length x) = 0 ===> Seq.isEmpty x",
"x = [] ===> List.isEmpty x",
"x = [||] ===> Array.isEmpty x",
"(List.length x) <> 0 ===> not (List.isEmpty x)",
"(Array.length x) <> 0 ===> not (Array.isEmpty x)",
"(Seq.length x) <> 0 ===> not (Seq.isEmpty x)",
"(List.length x) > 0 ===> not (List.isEmpty x)",
"(Array.length x) <> 0 ===> not (Array.isEmpty x)",
"(Seq.length x) <> 0 ===> not (Seq.isEmpty x)",
"List.concat (List.map f x) ===> List.collect f x",
"Array.concat (Array.map f x) ===> Array.collect f x",
"Seq.concat (Seq.map f x) ===> Seq.collect f x",
"List.isEmpty (List.filter f x) ===> not (List.exists f x)",
"Array.isEmpty (Array.filter f x) ===> not (Array.exists f x)",
"Seq.isEmpty (Seq.filter f x) ===> not (Seq.exists f x)",
"not (List.isEmpty (List.filter f x)) ===> List.exists f x",
"not (Array.isEmpty (Array.filter f x)) ===> Array.exists f x",
"not (Seq.isEmpty (Seq.filter f x)) ===> Seq.exists f x",
"List.length x >= 0 ===> true",
"Array.length x >= 0 ===> true",
"Seq.length x >= 0 ===> true",
"x = true ===> x",
"x = false ===> not x",
"true = a ===> a",
"false = a ===> not a",
"a <> true ===> not a",
"a <> false ===> a",
"true <> a ===> not a",
"false <> a ===> a",
"if a then true else false ===> a",
"if a then false else true ===> not a",
"not (not x) ===> x",
"(fst x, snd x) ===> x",
"true && x ===> x",
"false && x ===> false",
"true || x ===> true",
"false || x ===> x",
"not true ===> false",
"not false ===> true",
"fst (x, y) ===> x",
"snd (x, y) ===> y",
"List.fold f x [] ===> x",
"Array.fold f x [||] ===> x",
"List.foldBack f [] x ===> x",
"Array.foldBack f [||] x ===> x",
"x - 0 ===> x",
"x * 1 ===> x",
"x / 1 ===> x",
"List.fold (+) 0 x ===> List.sum x",
"Array.fold (+) 0 x ===> Array.sum x",
"Seq.fold (+) 0 x ===> Seq.sum x",
"List.sum (List.map x y) ===> List.sumBy x y",
"Array.sum (Array.map x y) ===> Array.sumBy x y",
"Seq.sum (Seq.map x y) ===> Seq.sumBy x y",
"List.average (List.map x y) ===> List.averageBy x y",
"Array.average (Array.map x y) ===> Array.averageBy x y",
"Seq.average (Seq.map x y) ===> Seq.averageBy x y",
"(List.take x y, List.skip x y) ===> List.splitAt x y",
"(Array.take x y, Array.skip x y) ===> Array.splitAt x y",
"(Seq.take x y, Seq.skip x y) ===> Seq.splitAt x y",
"List.empty ===> []",
"Array.empty ===> [||]",
"x::[] ===> [x]",
"pattern: x::[] ===> [x]",
"x @ [] ===> x",
"List.isEmpty [] ===> true",
"Array.isEmpty [||] ===> true",
"fun _ -> () ===> ignore",
"fun x -> x ===> id",
"id x ===> x",
"id >> f ===> f",
"f >> id ===> f",
"x = null ===> isNull x",
"null = x ===> isNull x",
"x <> null ===> not (isNull x)",
"null <> x ===> not (isNull x)",
"Array.append a (Array.append b c) ===> Array.concat [|a; b; c|]"
],
"ignore": []
}
}