Skip to content

A better partial function ? #2

@tonyhallett

Description

@tonyhallett

Unlike your partial code, the code below will return a function with multiple parameters rather than a single parameter of type list. Perhaps you would like to add it to your module.

shared
Record.RemoveFirstN = (record as record,numToRemove as number) =>
    let
        FieldNames = Record.FieldNames(record),
        RemoveFieldNames = List.FirstN(FieldNames,numToRemove)
    in
        Record.RemoveFields(record,RemoveFieldNames);

shared
_.Partial = 
        (f as function, a as list) => 
            let
                GetMinNumberOfArgs = (numArgs as number, numRequired as number, numFixed as number) as number =>
                    let 
                        NumRemaining = numArgs - numFixed,
                        MinArgs = if numArgs=numRequired then NumRemaining else
                            if numRequired < numFixed then 0
                            else
                                numRequired - numFixed
                    in
                        MinArgs,
                GetPartialSignature = (functionType as type,numFixed as number,parameters as record) =>
                    let
                        ReturnType = Type.FunctionReturn(functionType),
                        RemainingParameters = Record.RemoveFirstN(parameters,numFixed)
                    in
                        [ReturnType = ReturnType,Parameters = RemainingParameters],
                CreatePartialFunction = (newFunctionType as type,fixed as list) => 
                    let
                        ToExecute = (remaining as list) => Function.Invoke(f, fixed & remaining),
                        Partial = Function.From(newFunctionType,ToExecute)
                    in
                        Partial,

                NumFixed = List.Count(a),
                FunctionType = Value.Type(f),
                NumRequiredParameters = Type.FunctionRequiredParameters(FunctionType),
                Parameters = Type.FunctionParameters(FunctionType),
                

                MinNumberOrArgs = GetMinNumberOfArgs(Record.FieldCount(Parameters),NumRequiredParameters, NumFixed),
                NewFunctionType = Type.ForFunction(GetPartialSignature(FunctionType,NumFixed,Parameters),MinNumberOrArgs),

                PartialFunction = CreatePartialFunction(NewFunctionType,a)
            in
                PartialFunction;

Accompanying tests ( needs some additional set up code as per power query unit testing

shared MyExtension.UnitTest =
[
    
    TestFn =  (a as number, b as number, optional opt as number) => 
                let 
                    NotOptional = if opt <> null then opt else 0,
                    Result = a + b + NotOptional
                in
                        Result,
    PartialTest1 = _.Partial(TestFn,{9}),
    PartialTest2 = _.Partial(TestFn,{9,1}),
   
    facts =
    {        
        Fact("Partial fixed 1 parameter - execute don't provide optional",
             10,
             PartialTest1(1)
		),
        Fact("Partial fixed 1 parameter - execute provide optional",
             12,
             PartialTest1(1,2)
		),
        Fact("Partial fixed 2 parameters - execute provide optional",
             13,
             PartialTest2(3)
		),
        Fact("Partial fixed 2 parameters - execute don't provide optional",
             10,
             PartialTest2()
		),
        Fact("Should throw when don't provide required",
             "1 arguments were passed to function which expects between 2 and 3.",
             let
                 Errored = try
                    PartialTest1()
             in
                 Errored[Error][Message]
		),
        Fact("Should throw when provide incorrect parameter type",
             "We cannot convert the value ""incorrect type"" to type Number.",
             let
                 Errored = try
                    PartialTest1("incorrect type")
             in
                 Errored[Error][Message]
		),
        Fact("Record.RemoveFirstN 1",
             [Second="Second",Third="Third"],
             Record.RemoveFirstN([First="First",Second="Second",Third="Third"],1)
		),
        Fact("Record.RemoveFirstN 2",
             [Third="Third"],
             Record.RemoveFirstN([First="First",Second="Second",Third="Third"],2)
		)
    },

    report = Facts.Summarize(facts)
][report];


Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions